/*
 *----------------------------------------------------------------------------
 *	LSI Corporation
 *	1621 Barber Lane
 *	Milpitas, California 95035
 *----------------------------------------------------------------------------
 * Copyright © 2004-2006, LSI Corporation All Rights Reserved.
 * 
 * LSI's source code is an unpublished work and the use of copyright
 * notice does not imply otherwise. This source code contains confidential,
 * trade secret material of LSI Corporation. Any attempt or
 * participation in deciphering, decoding, reverse engineering or
 * in any way altering the source code is strictly prohibited, unless the
 * prior written consent of LSI Corporation.
 *---------------------------------------------------------------------------- 
 */

/** 
 *@file linux_osl.c
 *
 *@brief
 * This file contains the Linux OSL implementation.
 *
 * Linux Operating System related implementation is present in this file. This
 * file will contain driver entry points and the call back functions to the
 * SCSI Mid Layer. Linux Operating system Layer functions are also implemented
 * in this file 
 * 
 *@bug
 *	None
 *
 *@warning
 *	None
 *
 */

#include "linux_osl.h"
#include "osl_char.h"
#include "../../cmn_defs.h"
#include "../../oss/oss_iface.h"
#include "../../swr5_pciids.h"

#ifndef __devinit
#define __devinit
#endif

#ifndef __devexit
#define __devexit
#endif

#ifndef __devexit_p
#define __devexit_p(x) (x)
#endif

#define LINOSL_MAX_IDS					20

/* For poll mode support */
int megasr_poll_wait_aen;

typedef struct _adapter_t adapter_t;
extern int			adp_count_g ;
extern adapter_t	*adps_g[];

MODULE_AUTHOR		("LSI Corporation");

#ifdef MODULE_VERSION
MODULE_VERSION(LSIRAID_VERSION_STRING);
#endif /*MODULE_VERSION*/

MODULE_DESCRIPTION	("LSI Software RAID5 Driver");
MODULE_LICENSE		("LSI Proprietary");

uint32_t	dbglvl_g = DRIVER_DBG_LEVEL;
uint32_t	megasr_raid1_double_buffer = 0;	// We do not do RAID 1 double buffering on Linux platform
struct fasync_struct *lsraid_async_queue;
//static DECLARE_MUTEX(lsraid_async_queue_mutex);


#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)
	module_param_named(dbglvl_g, dbglvl_g, uint, MEGASR_PERM_MASK);
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
	module_param(dbglvl_g, uint, MEGASR_PERM_MASK);
#else 
#ifdef MODULE
	MODULE_PARM			(dbglvl_g, "i");
#endif
#endif

MODULE_PARM_DESC	(dbglvl_g, "driver-wide debug flag");

/* supported controller ids */
static struct pci_device_id lsraid_pci_tbl_g[] = SWR5_PCI_IDS;
MODULE_DEVICE_TABLE(pci, lsraid_pci_tbl_g);

extern uint8_t num_pci_dev_table_entries_g;

spinlock_t scu_lock;
#ifdef __VMKERNEL_MODULE__
spinlock_t io_request_lock;
#endif

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
static struct scsi_host_template	driver_template = LSI_RAID_TEMPLATE;
static struct scsi_host_template	*lsraid_template_gp	= &driver_template;
#else
static Scsi_Host_Template	driver_template = LSI_RAID_TEMPLATE;
static Scsi_Host_Template	*lsraid_template_gp	= &driver_template;
#endif

/* Function Prototypes */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19)
static LSI_RAID_IRQRETURN_T lsraid_isr(int irq, void* os_cxt );
#else
static LSI_RAID_IRQRETURN_T lsraid_isr(int irq, void *devp, struct pt_regs *regs);
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,0,0)
//MSIX vector for SCU controller
static LSI_RAID_IRQRETURN_T lsraid_msix_isr(int vector, void* os_cxt);
//Added for MSIX error vector
static LSI_RAID_IRQRETURN_T lsraid_err_isr(int vector, void* os_cxt);
#endif

//FW
static mem_fr_st_t mem_free_st_t;

/* helper functions */
static int __devinit lsraid_probe_one(struct pci_dev*, 
		const struct pci_device_id *);
static void lsraid_detach_one(struct pci_dev *dev);
void __lsraid_shutdown(void *adp);
static void	extract_pciconfig (struct pci_dev*, pvt_data_t *);
void remove_mem_allocated_for_memmgr(linux_adp_t	*lxadp);
boolean_t allocate_static_mem_and_add_to_memmgr(linux_adp_t *lxadp);
static uint32_t	scmd_to_rclpkt (struct scsi_cmnd*, rcl_packet_public_t*, struct pci_dev *);
static void_t osl_destroy_all_timers(linux_adp_t *lxadp);
void	osl_timeout (unsigned long);

void_t osl_cmd_pkt_success_handler(pvt_data_t* os_cxt, pvt_data_t* rclp_ptr);

rcl_packet_public_t *rcl_alloc_rcl_packet(void *adp);
boolean_t	osl_cmn_abort_cmd(void *adp, rcl_packet_public_t *rcl_pkt);
boolean_t	osl_cmn_reset(void *adp, uint8_t reset_lvl, device_id_t *device_id);
uint32_t megasr_lib_isr(void_t *adp, uint8_t msix_flag);
uint32_t megasr_lib_enable_intr(void_t *adp);
uint32_t megasr_lib_disable_intr(void_t *adp);

void_t osl_cmn_dump_busy_queue(void *adp, uint32_t rcl_error_status);
uint32_t megasr_lib_get_adp_size(void);
uint8_t  megasr_get_adp_type(void *adp);
uint8_t megasr_lib_get_adp_type_scu(void *adp);
uint8_t megasr_lib_get_multi_controller(void *adp); 
void megasr_lib_set_msix_mode(void *adp);
void megasr_lib_clear_msix_mode(void *adp);
uint8_t megasr_lib_get_msix_mode(void *adp);
void megasr_lib_set_lxadp(void *adp, void *lxadp);
extern uint32_t megasr_init_adp(pvt_data_t *adpv, uint8_t bus_number, uint8_t dev_no, uint8_t fn_no);

uint32_t	rcl_scan_devices(void *adp);
//void osl_log_adp_pci_info(void *adp);
uint32_t	rcl_read_and_populate_cfg(void *adp);
uint32_t	rcl_set_pwr_state(adapter_t *adp, uint8_t pwr_lvl, boolean_t in_steps);
void_t rcl_unload(void *adp);
void_t		rcl_shutdown(void *adp);
void_t		rcl_mgr_resume_all_st(adapter_t *adp);
void_t		rcl_mgr_restore_pd_cache_settings(adapter_t *adp);
void_t		rcl_mgr_set_pd_wce(adapter_t *adp);
void *lin_get_adp_pci_config_space_ptr_and_size(void *adpv, uint32_t *cfg_size);
void lin_set_adp_pci_config(uint32_t base_addr,int i, void *adp);
void_t rcl_free_rcl_packet(pvt_data_t *adp, rcl_packet_public_t *rclp);
pvt_data_t * lin_get_linux_adp(void *data);
uint32_t	rcl_init_adapter(pvt_data_t *adp);
boolean_t mem_mgr_add_sgl_mem(pvt_data_t *adapter, cmn_sgl_t *sgl, pvt_data_t
*cxt);
boolean_t lin_add_allocated_sgl_mem(pvt_data_t *adapter, cmn_sgl_t *sgl);
boolean_t lin_get_sgl_mem_to_free(pvt_data_t *adapter, cmn_sgl_t *sgl,uint8_t offset);

void mem_mgr_fini_mem_mgr(pvt_data_t *adapter);
uint32_t rcl_is_r5rw_or_noniocmd(pvt_data_t *adp, rcl_packet_public_t *rclp);
uint32_t rcl_is_nonld_or_iocmd(adapter_t *adp, rcl_packet_public_t *rclp);
uint32_t rcl_is_driver_busy(adapter_t *adp);
void lin_dec_cmd_in_rcl_cnt(void *adp);
void megasr_lib_get_ven_dev_id_from_pci_dev_tbl(uint32_t dev_ind,
									uint16_t *venid, uint16_t *devid);

void lin_set_rclp_ld_ptr(pvt_data_t *adpv, pvt_data_t *rclpv, device_id_t *id);
uint32_t lin_is_ld_busy(pvt_data_t *rclpv);
uint8_t megasr_lib_is_strip_overflow(pvt_data_t *rclpv);
uint32_t megasr_lib_get_strip_sectors(pvt_data_t *rclpv);
void megasr_lib_set_low_mem_condition(pvt_data_t *adpv);
uint32_t megasr_lib_get_rcl_packet_size(void);

void megasr_lib_init_adp(pvt_data_t *adpv, short *cmd_per_lun, short unsigned int *max_sectors, uint32_t adp_ind);
void_t		osl_cmn_process_rclp(pvt_data_t *adp, pvt_data_t *rclp);
uint8_t		osl_cmn_fill_sense_info_from_rclp(pvt_data_t *rclp, void_t *sense_data, uint8_t sense_len);
void_t		rcl_authenticate_ibutton(pvt_data_t *adp);
void_t		osl_cmn_flush_schedule_queue(pvt_data_t *adp);

void_t mem_mgr_init_mem_mgr(void *adapter, uint32_t node_size);
void mem_mgr_override_dynamic_cache_size(pvt_data_t *os_context, uint32_t node_size);
uint8_t mem_mgr_add_to_mem_pool(void_t *adp, uint8_t *ptr, uint8_t *phy_ptr, uint32_t tot_len, mem_pool_type_e pool_type);
uint32_t mem_mgr_get_desc_size_for_sgl_mem(pvt_data_t *adapter, uint32_t sgl_size);
void mem_fre_tasklet ( unsigned long );
uint32_t rcl_ioctl_set_adp_cmd_unblock(adapter_t * adp, int is_callback);
uint32_t rcl_fw_dwld_app_abort(adapter_t * adp,int reserved);
void lin_osl_set_busy(pvt_data_t *os_context);
void lin_osl_unset_busy(pvt_data_t *os_context);

	


DECLARE_TASKLET ( mf_tasklet, mem_fre_tasklet, ( unsigned long ) &mem_free_st_t );


/*
 * When we are asked to load on a controller that we didn't know about at compile time,
 * we use the megasr_adapter_class value to know the type of the controller. Users can
 * change this when they compile shim package
 */
megasr_adapter_class_t megasr_adapter_class = MEGASR_DYNAMIC_PCIIDS_DEFAULT_ADP_CLASS;

#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)

static int lsraid_reboot_notify (struct notifier_block *this, 
		unsigned long code, void *unused);

static struct notifier_block lsraid_reboot_notifier = {
	notifier_call : lsraid_reboot_notify
};

static struct proc_dir_entry	*megasas_proc_root;
static struct proc_dir_entry	*megasas_proc_hba_map;
static struct proc_dir_entry	*megasas_proc_version;
static struct proc_dir_entry	*megasas_proc_release_date;

static int megasas_proc_show_version(char *page, char **start, off_t offset,
		int count, int *eof, void *data)
{	
	*eof = 1;
	return (snprintf(page, strlen(LSIRAID_VERSION_STRING)+2, "%s\n",
				LSIRAID_VERSION_STRING));
}

static int megasas_proc_show_release_date(char *page, char **start, off_t
		offset, int count, int *eof, void *data)
{
	*eof = 1;
	return (snprintf(page, strlen(LSIRAID_DRIVER_RELEASE_DATE)+2, "%s\n",
				LSIRAID_DRIVER_RELEASE_DATE));
}

static int megasas_proc_show_bus_devfn(char *page, char **start, off_t offset,
		int count, int *eof, void *data)
{
	int		i;
	uint32_t	bus_devfn = 0;
	linux_adp_t *lxadp = (linux_adp_t *)data;

	bus_devfn = ((lxadp->pdev->bus->number) << 16 |
		(PCI_SLOT(lxadp->pdev->devfn)) << 8 |
		(PCI_FUNC(lxadp->pdev->devfn))) & 0xFFFFFF;

	*eof = 1;
	return (sprintf(page, "%d", bus_devfn));
}

static void megasas_create_proc_entry(void *lxp)
{
	linux_adp_t			*lxadp = (linux_adp_t *)lxp;
	unsigned char		string[8] = { 0 };

	sprintf(string, "%d", lxadp->host->host_no);
	lxadp->hba_proc_dir = proc_mkdir(string, megasas_proc_hba_map);

	if (!lxadp->hba_proc_dir) {
		con_log(CL_ERR, ("megaswr[osl]:   proc creation failed\n"));
		return;
	}

	create_proc_read_entry("bus_devfn", S_IRUSR | S_IFREG,
			lxadp->hba_proc_dir, megasas_proc_show_bus_devfn, lxadp );
}

static void megasas_remove_proc_entry(pvt_data_t *lxp)
{
	linux_adp_t		*lxadp = (linux_adp_t *)lxp;
	unsigned char		string[8] = { 0 };

	remove_proc_entry("bus_devfn", lxadp->hba_proc_dir);

	sprintf(string, "%d", lxadp->host->host_no);
	remove_proc_entry(string, megasas_proc_hba_map);
}

static void megasr_setup_proc(void)
{
	megasas_proc_root = proc_mkdir( LSIRAID_DRIVER_SIGNATURE, proc_scsi );

	if (megasas_proc_root) {
		megasas_proc_version = create_proc_read_entry( "version",
				S_IRUSR|S_IFREG, megasas_proc_root, megasas_proc_show_version,
				NULL);

		if (!megasas_proc_version){ 
			con_log(CL_ERR, ("megaswr[osl]:  cann't create_read_version p_version=%p\n", megasas_proc_version));
		}

		megasas_proc_release_date = create_proc_read_entry( "release_date",
				S_IRUSR | S_IFREG, megasas_proc_root,
				megasas_proc_show_release_date, NULL);

		megasas_proc_hba_map = proc_mkdir("hba_map",megasas_proc_root);
	}
}

#endif

/**
 * @brief
 * Private implementation for pci_alloc_consistent.
 * We do not want the GFP_ATOMIC flag to be set if caller can afford to sleep
 *
 * @param hwdev		: pointer to our device object
 * @param size		: size (in bytes) of memory required
 * @param linux_dma	: internal dma object tracker
 * @param wait		: are we allowed to sleep
 **/
uint32_t megasr_dma_alloc(struct pci_dev *hwdev, size_t size, linux_dma_mem_list_t *linux_dma, uint8_t wait)
{
	void	*rc;

	// try to use the generic API first, to get the memory.
	// If that fail's, force use of the GFP_KERNEL flag
	rc = pci_alloc_consistent(hwdev, size, &linux_dma->dma_addr);

	if (rc) {
		linux_dma->vir	= (uint8_t *)rc;
		linux_dma->len	= (uint32_t)size;
		linux_dma->flag	= LINUX_DMA_MEM_PCI_CONSISTENT;
		memset(linux_dma->vir, 0, linux_dma->len);
		return LSI_TRUE;
	}
	else if (wait == LINUX_DMA_NOWAIT) {
		// Cannot make the next calls if caller cannot sleep
		return LSI_FALSE;
	} /* else fall through */

	// Generic API failed to allocate memory, switch to alternate
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
	rc = dma_alloc_coherent(hwdev == NULL ? NULL : &hwdev->dev, size, &linux_dma->dma_addr, GFP_KERNEL|__GFP_REPEAT);
	if (rc != NULL) {
		linux_dma->vir		= (uint8_t *)rc;
		linux_dma->len		= (uint32_t)size;
		linux_dma->flag		= LINUX_DMA_MEM_PCI_COHERENT;
		memset(linux_dma->vir, 0, linux_dma->len);
		return LSI_TRUE;
	}
#endif	
	return LSI_FALSE;
}

void megasr_dma_free(struct pci_dev *hwdev, linux_dma_mem_list_t *linux_dma)
{
	switch (linux_dma->flag) {

	case LINUX_DMA_MEM_PCI_CONSISTENT:
		pci_free_consistent(hwdev, (size_t)linux_dma->len, linux_dma->vir, linux_dma->dma_addr);
		break;

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
	case LINUX_DMA_MEM_PCI_COHERENT:
		dma_free_coherent(&hwdev->dev, (size_t)linux_dma->len, linux_dma->vir, linux_dma->dma_addr);
		break;
#endif

	default:
		con_log(CL_ERR, ("megasr[%s]: invalid dma memory type for de-allocation\n", __FUNCTION__));
	}

	return;
}

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)
static void lsraid_shutdown(struct pci_dev *dev);
#else
static void lsraid_shutdown(struct device *dev);
#endif
#endif

uint32_t megasr_msix_enable (linux_adp_t *lxadp, struct pci_dev *pdev) 
{
	uint32_t status = LSI_FALSE;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,0,0)
	uint8_t num_msix = 2; //Default MSIX count is 2
	uint8_t	i;
	
	num_msix = num_msix + (num_msix * megasr_lib_get_multi_controller(lxadp->adp)); 
	con_log(CL_WARN,("megaswr[osl]: Enabling %d MSIX for SCU\n", num_msix));
	for (i = 0; i < num_msix; i++) {
		lxadp->msix_intr[i].entry = i;
		lxadp->msix_intr[i].vector = 0;
	}

	status = pci_enable_msix(pdev, lxadp->msix_intr, num_msix);
	if(status) return status;
			
	for (i = 0; i < num_msix; i++) {
		if (i & 1) {
			status = request_irq( lxadp->msix_intr[i].vector, lsraid_err_isr, IRQF_SHARED, "megasr", lxadp);
		}
		else {
			status = request_irq( lxadp->msix_intr[i].vector, lsraid_msix_isr, IRQF_SHARED, "megasr", lxadp);
		}
		con_log(CL_ANN,("megaswr[osl]: MSIX[%d]: Status:%d Vector:%d \n", i, status, lxadp->msix_intr[i].vector));
		if (!status) {
			continue;
		}
		else {
			while (i--) {
				free_irq(lxadp->msix_intr[i].vector, lxadp);
			}
			break;
		}
	}
#else
	con_log(CL_ERR,("megaswr[osl]: Trying to set MSIX for < 3.0 kernel\n"));
#endif
	return status;
}

void megasr_free_irq (linux_adp_t *lxadp, struct pci_dev *pdev) 
{
	uint8_t num_msix = 2; //Default MSIX count is 2
	
	if (megasr_lib_get_msix_mode(lxadp->adp)) {
		con_log(CL_OSL,("megaswr[osl]: Free MSIX vectors\n"));
		num_msix = num_msix + (num_msix * megasr_lib_get_multi_controller(lxadp->adp));
		while (num_msix--) {
			free_irq(lxadp->msix_intr[num_msix].vector, lxadp);
		}
		pci_disable_msix(pdev);
	}
	else {
		con_log(CL_OSL,("megaswr[osl]: Free irq line\n"));
		free_irq(pdev->irq, lxadp);
	}
}

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18)
int megasr_resume( struct pci_dev *pdev)
{
	struct Scsi_Host *host = pci_get_drvdata(pdev);
	adapter_t *adp = (adapter_t*)((linux_adp_t*)(host->hostdata[0]))->adp;
	linux_adp_t *lxadp = (linux_adp_t*)(host->hostdata[0]);
	//unsigned long flags;
	unsigned int rval = 0;
	
	con_log(CL_INFO,("megaswr:  megasr_resume  lxadp addr = %x timer count =%d..\n", lxadp, lxadp->scu_timer_count));
	// PCI specific OS calls in Linux
	pci_set_power_state(pdev, PCI_D0);
	pci_enable_wake(pdev, PCI_D0, 0);
	pci_restore_state(pdev);
	rval = pci_enable_device(pdev);

	if (rval) {
		con_log(CL_WARN, ("megasr: Enable device failed\n"));
    }
	con_log(CL_INFO, ("megasr: megasr_resume PCI OS calls complete\n"));
	megasr_lib_clear_msix_mode(adp);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,0,0)
	if(megasr_lib_get_adp_type_scu(adp)) {
		megasr_lib_set_msix_mode(adp);
	}
#endif
	// HAL layer reinitialization
	rcl_set_pwr_state(adp, PM_S0, LSI_FALSE);
	
	if(megasr_lib_get_msix_mode(lxadp->adp)){
		rval = megasr_msix_enable (lxadp, pdev);
	}
	else {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)
		rval = request_irq(pdev->irq, lsraid_isr, IRQF_SHARED, "megasr", lxadp);
#else
		rval = request_irq(pdev->irq, lsraid_isr, SA_SHIRQ, "megasr", lxadp); 
#endif
	}	
	if (rval){
		con_log(CL_WARN,("megaswr[osl]: request_irq failed: %d\n", rval));
		return LSI_FALSE;
	}
	// Enable interrupts and resume background operations
	megasr_lib_enable_intr(lxadp->adp);

	// Enable PD Wrtie cache for drives under rebuild 
	rcl_mgr_set_pd_wce(adp);

	// Resume special tasks (if needed)
	rcl_mgr_resume_all_st(adp);

	return LSI_TRUE;
}

int megasr_suspend(struct pci_dev *pdev, pm_message_t state)
{
	struct Scsi_Host *host = pci_get_drvdata(pdev);
	adapter_t *adp = (adapter_t*)((linux_adp_t*)host->hostdata[0])->adp;
	linux_adp_t *lxadp = (linux_adp_t*)(host->hostdata[0]);
	unsigned long flags;
	
	//TODO: Verify whether this delay is still mandatory?
	udelay(1000);
        oss_spin_lock_irqsave(adp, &flags);
	// Purge all the IO's and stop the controller.
	rcl_shutdown(adp);
        oss_spin_unlock_irqrestore(adp, flags);
	con_log(CL_INFO, ("megasr: megasr_suspend Disabling Interrupts\n"));
	//oss_scu_destroy_all_timers(lxadp);
	megasr_lib_disable_intr(lxadp->adp);
	// Release the IRQ handler
	megasr_free_irq(lxadp, lxadp->pdev);
	//OS specific PCI calls in Linux
	pci_save_state(pdev);
	pci_disable_device(pdev);
	pci_set_power_state(pdev, pci_choose_state(pdev, state));
	//oss_scu_destroy_all_timers(lxadp);
	con_log(CL_INFO,("megaswr:  megasr_suspend done Returning..\n"));
	return LSI_TRUE;
}
#endif

static struct   pci_driver lsraid_pci_driver_g_ops = {
	.name		= LSIRAID_DRIVER_SIGNATURE,
	.id_table	= lsraid_pci_tbl_g,
	.probe		= lsraid_probe_one,
	.remove		= __devexit_p(lsraid_detach_one),
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)
	.shutdown	= lsraid_shutdown,
#else
	.driver		= {
		.shutdown = lsraid_shutdown,
	}
#endif

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18) // Mainly for RHEL5 Gold
	.resume		= megasr_resume,
	.suspend	= megasr_suspend,
#endif
#endif
};

/**
 *@brief
 * Driver module entry point for 2.6 kernel.
 * This is called while driver is loading. This function prepares the PCI ID table
 * of the supported controllers and register driver module with the kernel.
 * This function also loads the character driver to support IOCTLs.
 *
 *@param		None
 *@return 		zero if function succeeds
 *@return 		no-zero if function fails
 */
static int __init lsraid_init(void)
{
	int	error;
	
	spin_lock_init(&scu_lock);
#ifdef __VMKERNEL_MODULE__
	if (!vmk_set_module_version(LSIRAID_VERSION_STRING)) {
		return 0;
	}

	spin_lock_init(&io_request_lock);
	driver_template.driverLock = &io_request_lock;
#endif

	/* Announce the Driver Version */
	LSI_RAID_DISPLAY_DRIVER_VERSION;

#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
	megasr_setup_proc();

	/* Register the Shutdown Notification hook in kernel */
	if(register_reboot_notifier(&lsraid_reboot_notifier)) {
		con_log(CL_ERR, ("megaswr[osl]:  reboot_notifier not registered!\n"));
	}
#endif

	error = pci_register_driver(&lsraid_pci_driver_g_ops);

	if (error < 0 ) {

#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
		unregister_reboot_notifier(&lsraid_reboot_notifier);

		if (megasas_proc_root) {
			remove_proc_entry("version", megasas_proc_root);
			remove_proc_entry("release_date", megasas_proc_root);
			remove_proc_entry("hba_map", megasas_proc_root);
			remove_proc_entry(LSIRAID_DRIVER_SIGNATURE, proc_scsi);
		}
#endif
		con_log(CL_ERR, ("megaswr[osl]: driver initialization error...\n"));
#ifdef __VMKERNEL_MODULE__
		spin_lock_destroy(&io_request_lock);
#endif
		return error;
	}
#ifdef __VMKERNEL_MODULE__
	else {
		scsi_register_module(MODULE_SCSI_HA, &driver_template);
	}
#endif
	
	lsraid_register_char_driver();

	return 0;
}

/**
 *@brief
 * Driver module exit point for 2.6 kernel.
 * This is called by the kernels while unloading the driver. This function
 * will unregister character driver loaded for supporting IOCTLs and
 * unregister the driver module itself from the kernel.
 * 
 *@param	None
 *@return 	None	
 */
static void __exit lsraid_exit(void)
{
	lsraid_unregister_char_driver();
	pci_unregister_driver(&lsraid_pci_driver_g_ops);
#ifdef __VMKERNEL_MODULE__
	scsi_unregister_module(MODULE_SCSI_HA, &driver_template);
	spin_lock_destroy(&io_request_lock);
#endif
}

/**
 *@brief
 * Driver shut down routine in for 2.6 kernel.
 * This function make use of generic shut down routine for both 2.6 and 2.4
 * kernels. This is called by the kernel when the system reboots and it is
 * done to let the lower level drivers to take all house keeping actions like
 * flushing the cache.
 * 
 *@param	dev		pointer to device structure
 *@return	None
 */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)

static void lsraid_shutdown(struct pci_dev *dev)
{
	struct Scsi_Host *host = pci_get_drvdata(dev);

	__lsraid_shutdown(((linux_adp_t *)host->hostdata[0])->adp);
}

#else /*LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)*/

static void lsraid_shutdown(struct device *dev)
{
	struct Scsi_Host *host = pci_get_drvdata(to_pci_dev(dev));

	__lsraid_shutdown(((linux_adp_t *)host->hostdata[0])->adp);
}

#endif /*LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)*/
#endif

#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)

/* Function Prototypes used only by 2.4 kernel */
/**
 *@brief
 * LSI MegaSWR release adapter
 * This function is used to unload MegaSWR driver for an adapter. Release
 * funtion will be called for each adapter present in the system. This is
 * called when the driver unloaded by using a rmmod command. This function
 * should cal reboot notifier of the driver when driver for all the
 * controllers are removed and it should unregister the character driver too.
 * 
 *@param	host	Pointer to scsi midlayer host structure
 *@return	0		If Success 
 */
int lsraid_release(struct Scsi_Host* host)
{
	linux_adp_t	*lxadp = (linux_adp_t *)host->hostdata[0];

	lsraid_detach_one(lxadp->pdev);
	
	if (!adp_count_g) {
		unregister_reboot_notifier(&lsraid_reboot_notifier);
		lsraid_unregister_char_driver();
	}

	return 0;
}

/**
 *@brief
 * This routine will be called when the user has done a forced shutdown on the
 * system. Flush the disks cache. This is called by the kernel when the system 
 * reboots and it is done to let the lower level drivers to take all house
 * keeping actions like flushing the cache.
 *
 *@param	this	Notifier Block structure pointer	
 *@param	code	shutdown code
 *@param	nused	
 *@return	NOTIFY_DONE after finishing 
 */
static int lsraid_reboot_notify (struct notifier_block *this, 
		unsigned long code, void *unused)
{
	int		i;

	con_dbg(CL_ANN, ("megaswr[osl]:  reboot notifier called\n"));

	for (i = 0; i < adp_count_g; i++) {
		__lsraid_shutdown(adps_g[i]);
	}

	return NOTIFY_DONE;
}
#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) */

/**
 *@brief
 * LSI megasr info 
 * This function provide information string about the driver to the SCSI mid
 * layer.
 *
 *@param	host	megasr host structure pointer
 *@return	Driver information string
 *@remarks
 * This is a driver entry function
 */
const char *lsraid_info(struct Scsi_Host *host)
{
	return LSIRAID_DRIVER_NAME;
}

/**
 * notification routine for partial IO completion.
 *
 * @param os_context	: Pointer to the OS context.
 * @param rclp_ptr	: Pointer to the raid packet object.
 *
 * @return void.
 **/
void_t megasr_linux_process_low_mem_io_finalize(pvt_data_t* os_cxt, pvt_data_t* rclp_ptr)
{
	rcl_packet_public_t	*rclp_split;
	rcl_packet_public_t	*rclp;
	linux_adp_t 		*linux_adp;

	linux_adp	= (linux_adp_t *)lin_get_linux_adp(os_cxt);
	rclp_split	= (rcl_packet_public_t *)rclp_ptr;
	rclp		= (rcl_packet_public_t *)rclp_split->os_packet;

	rclp->status		= rclp_split->status;
	rclp->scsi_status	= rclp_split->scsi_status;
	rclp->sense_info	= rclp_split->sense_info;

	if (rclp_split->status == STS_SUCCESS) {
		rclp->success_handler(linux_adp->adp, rclp);
	}
	else {
		rclp->failure_handler(linux_adp->adp, rclp);
	}

	linux_adp->rclp_crash_inuse	= 0;
}

/**
 * notification routine for partial IO completion for first few sectors.
 *
 * @param os_context	: Pointer to the OS context.
 * @param rclp_ptr	: Pointer to the raid packet object.
 *
 * @return void.
 **/
void_t megasr_linux_process_low_mem_io_notify(pvt_data_t* os_cxt, pvt_data_t* rclp_ptr)
{
	rcl_packet_public_t	*rclp_split;
	rcl_packet_public_t	*rclp;
	linux_adp_t 		*linux_adp;
	scsi_packet_t		*rclp_sp;
	scsi_packet_t		*rclp_split_sp;
	uint32_t		total_skip_bytes;
	uint32_t		sge_skip_bytes;
	uint32_t		sge_available_bytes;
	uint32_t		buffer_len_so_far;
	uint32_t		rclp_sg_index;
	uint32_t		split_sg_index;
	//4k changes
	//rcl_packet_t *rclp_tmp	= (rcl_packet_t *)rclp_ptr;
	
	linux_adp	= (linux_adp_t *)lin_get_linux_adp(os_cxt);
	rclp_split	= (rcl_packet_public_t *)rclp_ptr;
	rclp		= (rcl_packet_public_t *)rclp_split->os_packet;

	if (rclp_split->status != STS_SUCCESS) {
		// Some error happened during partial command processing. Let
		// main raid packet take care of it.
		rclp->status			= rclp_split->status;
		rclp->scsi_status		= rclp_split->scsi_status;
		rclp->sense_info		= rclp_split->sense_info;
		linux_adp->rclp_crash_inuse	= 0;
		rclp->failure_handler(linux_adp->adp, rclp);
		return;
	}

	// Process rest of the command
	//4k changes
	//total_skip_bytes		= rclp_split->blk_count << LSIRAID_SECTOR_SHIFT_N(rclp_tmp->ld->bytes_per_block);
	rclp_split->success_handler	= megasr_linux_process_low_mem_io_finalize;
	rclp_split->failure_handler	= megasr_linux_process_low_mem_io_finalize;
	rclp_split->os_packet		= rclp;

	rclp_split->start_blk		= rclp_split->start_blk + rclp_split->blk_count;
	rclp_split->blk_count		= rclp->blk_count - rclp_split->blk_count;
	//4k changes
    //rclp_split->buffer_len		= rclp_split->blk_count << LSIRAID_SECTOR_SHIFT_N(rclp_tmp->ld->bytes_per_block);

	rclp_split->cmd.packet_type	= SCSI_PACKET;

	rclp_sp		= &rclp->cmd.u.scsi_packet;
	rclp_split_sp	= &rclp_split->cmd.u.scsi_packet;

	memset(rclp_split_sp->u.lsi_cdb, 0,16);
	
	rclp_split_sp->u.lsi_cdb[0]	= rclp_sp->u.lsi_cdb[0];
	rclp_split_sp->cdb_len		= rclp_sp->cdb_len;

	switch (rclp_sp->cdb_len) {
	case 10 :
		SET_START_BLOCK(rclp_split_sp->u.lsi_cdb, rclp_split->start_blk); 
		SET_BLOCK_COUNT(rclp_split_sp->u.lsi_cdb, rclp_split->blk_count);
		break;
	case 16:
		SET_START_BLOCK16(rclp_split_sp->u.lsi_cdb, rclp_split->start_blk); 
		SET_BLOCK_COUNT16(rclp_split_sp->u.lsi_cdb, rclp_split->blk_count);
		break;
	case 6 :
		SET_START_BLOCK6(rclp_split_sp->u.lsi_cdb, rclp_split->start_blk); 
		SET_BLOCK_COUNT6(rclp_split_sp->u.lsi_cdb, rclp_split->blk_count);
		break;
	case 12 :
		SET_START_BLOCK12(rclp_split_sp->u.lsi_cdb, rclp_split->start_blk); 
		SET_BLOCK_COUNT12(rclp_split_sp->u.lsi_cdb, rclp_split->blk_count);
		break;
	}

	buffer_len_so_far	= 0;
	split_sg_index		= 0;
	sge_available_bytes	= 0;
	for (rclp_sg_index = 0; rclp_sg_index < rclp->cmn_sgl.sge_count; rclp_sg_index++) {
		buffer_len_so_far += rclp->cmn_sgl.sge[rclp_sg_index].length;

		if (buffer_len_so_far <= total_skip_bytes) continue;

		sge_available_bytes	= buffer_len_so_far - total_skip_bytes;
		break;
	}
	rclp_split->cmn_sgl.sge_count	= 1;
	rclp_split->cmn_sgl.sge[0]		= rclp->cmn_sgl.sge[rclp_sg_index];

	if (sge_available_bytes < rclp->cmn_sgl.sge[rclp_sg_index].length) {
		// Part of this node was used in last IO.
		sge_skip_bytes	= rclp->cmn_sgl.sge[rclp_sg_index].length - sge_available_bytes;
		rclp_split->cmn_sgl.sge[0].length	= sge_available_bytes;
		rclp_split->cmn_sgl.sge[0].vaddr	= rclp_split->cmn_sgl.sge[0].vaddr + sge_skip_bytes;
		rclp_split->cmn_sgl.sge[0].paddr	= rclp_split->cmn_sgl.sge[0].paddr + sge_skip_bytes;
	}

	split_sg_index++;
	rclp_sg_index++;

	for (;rclp_sg_index < rclp->cmn_sgl.sge_count; rclp_sg_index++, split_sg_index++) {
		rclp_split->cmn_sgl.sge[split_sg_index]		= rclp->cmn_sgl.sge[rclp_sg_index];
	}

	rclp_split->cmn_sgl.sge_count	= split_sg_index;

	// @todo: put under #ifdef DEBUG_BUILD once we know this piece of the code has been tested enough
	{
		uint32_t	rclp_split_sg_bytes;
		uint32_t	i;

		rclp_split_sg_bytes	= 0;

		for (i = 0; i < rclp_split->cmn_sgl.sge_count; i++) {
			rclp_split_sg_bytes	+= rclp_split->cmn_sgl.sge[i].length;
		}

		if (rclp_split_sg_bytes != rclp_split->buffer_len) {
			con_log(CL_ERR, ("DEBUG: logic error at %s:%d, %d:%d\n", __FUNCTION__, __LINE__, rclp_split_sg_bytes, rclp_split->buffer_len));

			for (i = 0; i < rclp_split->cmn_sgl.sge_count; i++) {
				con_log(CL_ANN,  ("2nd split sg[%d] length:%d\n", i, rclp_split->cmn_sgl.sge[i].length));
			}

			for (i = 0; i < rclp->cmn_sgl.sge_count; i++) {
				con_log(CL_ANN,  ("2nd org sg[%d] length:%d\n", i, rclp->cmn_sgl.sge[i].length));
			}
		}
	}

	osl_cmn_process_rclp(linux_adp->adp, rclp_split);
}

/**
 * process raid packet under low memory conditions.
 *
 * This routine is called to process raid packets under low memory
 * conditions. In addition, these packets spill over more than
 * one strip. We slice these off on strip boundary and send off
 * to raid core processing.
 *
 * @param linux_adp	: Pointer to the Linux adapter object.
 * @param rclp		: Original raid packet carrying the IO operation.
 * @param rclp_split	: The surrogate packet which will carry the split operation.
 *
 * @return void.
 **/
static void megasr_linux_process_low_mem_io(linux_adp_t *linux_adp, rcl_packet_public_t *rclp, rcl_packet_public_t *rclp_split)
{
	scsi_packet_t	*sp_rclp;
	scsi_packet_t	*sp_rclp_split;
	uint32_t	buffer_len_so_far;
	uint32_t	sg_index;
	uint32_t	extra_bytes;
	//4k changes
	//rcl_packet_t *rclp_tmp	= (rcl_packet_t *)rclp_split;

	rclp_split->success_handler	= megasr_linux_process_low_mem_io_notify;
	rclp_split->failure_handler	= megasr_linux_process_low_mem_io_notify;
	rclp_split->os_packet		= rclp;

	rclp_split->start_blk		= rclp->start_blk;
	rclp_split->device_id		= rclp->device_id;
	rclp_split->io_direction	= rclp->io_direction;

	rclp_split->blk_count		= megasr_lib_get_strip_sectors(rclp);
	//4k changes
	//rclp_split->buffer_len		= rclp_split->blk_count << LSIRAID_SECTOR_SHIFT_N(rclp_tmp->ld->bytes_per_block));//LSIRAID_SECTOR_SHIFT;

	rclp_split->cmd.packet_type	= SCSI_PACKET;

	sp_rclp		= &rclp->cmd.u.scsi_packet;
	sp_rclp_split	= &rclp_split->cmd.u.scsi_packet;

	memset(sp_rclp_split->u.lsi_cdb, 0,16);
	
	sp_rclp_split->u.lsi_cdb[0]	= sp_rclp->u.lsi_cdb[0];
	sp_rclp_split->cdb_len		= sp_rclp->cdb_len;

	switch (sp_rclp->cdb_len) {
	case 10 :
		SET_START_BLOCK(sp_rclp_split->u.lsi_cdb, rclp_split->start_blk); 
		SET_BLOCK_COUNT(sp_rclp_split->u.lsi_cdb, rclp_split->blk_count);
		break;
	case 16:
		SET_START_BLOCK16(sp_rclp_split->u.lsi_cdb, rclp_split->start_blk); 
		SET_BLOCK_COUNT16(sp_rclp_split->u.lsi_cdb, rclp_split->blk_count);
		break;
	case 6 :
		SET_START_BLOCK6(sp_rclp_split->u.lsi_cdb, rclp_split->start_blk); 
		SET_BLOCK_COUNT6(sp_rclp_split->u.lsi_cdb, rclp_split->blk_count);
		break;
	case 12 :
		SET_START_BLOCK12(sp_rclp_split->u.lsi_cdb, rclp_split->start_blk); 
		SET_BLOCK_COUNT12(sp_rclp_split->u.lsi_cdb, rclp_split->blk_count);
		break;
	}

	buffer_len_so_far = 0;
	for (sg_index = 0; sg_index < rclp->cmn_sgl.sge_count; sg_index++) {
		buffer_len_so_far += rclp->cmn_sgl.sge[sg_index].length;

		rclp_split->cmn_sgl.sge_count		= sg_index + 1;
		rclp_split->cmn_sgl.sge[sg_index]	= rclp->cmn_sgl.sge[sg_index];

		if (buffer_len_so_far < rclp_split->buffer_len)
			continue;
		else if (buffer_len_so_far == rclp_split->buffer_len)
			break;
		else {
			extra_bytes	= buffer_len_so_far - rclp_split->buffer_len;
			rclp_split->cmn_sgl.sge[sg_index].length	= rclp->cmn_sgl.sge[sg_index].length - extra_bytes;
			break;
		}
	}

	// @todo: put under #ifdef DEBUG_BUILD once we know this piece of the code has been tested enough
	{
		uint32_t	rclp_split_sg_bytes;
		uint32_t	i;

		rclp_split_sg_bytes	= 0;

		for (i = 0; i < rclp_split->cmn_sgl.sge_count; i++) {
			rclp_split_sg_bytes	+= rclp_split->cmn_sgl.sge[i].length;
		}

		if (rclp_split_sg_bytes != rclp_split->buffer_len) {
			con_log(CL_ERR, ("DEBUG: logic error at %s:%d, %d:%d\n", __FUNCTION__, __LINE__, rclp_split_sg_bytes, rclp_split->buffer_len));

			for (i = 0; i < rclp_split->cmn_sgl.sge_count; i++) {
				con_log(CL_ANN, ("1st split sg[%d] length:%d\n", i, rclp_split->cmn_sgl.sge[i].length));
			}

			for (i = 0; i < rclp->cmn_sgl.sge_count; i++) {
				con_log(CL_ANN, ("1st org sg[%d] length:%d\n", i, rclp->cmn_sgl.sge[i].length));
			}
		}
	}


	osl_cmn_process_rclp(linux_adp->adp, rclp_split);
}

void_t inline  get_dma_direction( rcl_packet_public_t *rclp, uint8_t *dma_dir ){

	switch (rclp->io_direction)	{
	
		case IODIR_TO_DEVICE:
			*dma_dir	= PCI_DMA_TODEVICE;
			break;

		case IODIR_FROM_DEVICE:
			*dma_dir	= PCI_DMA_FROMDEVICE;
			break;

		case IODIR_BIDIRECTIONAL:
		default:
			*dma_dir	= PCI_DMA_BIDIRECTIONAL;
			break;
	}
}

void_t linux_unmap_sg(linux_adp_t *lxadp, rcl_packet_public_t *rclp )
{
  struct scsi_cmnd	*scp = (struct scsi_cmnd *)rclp->os_packet;

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)
  scsi_dma_unmap( scp );
  return;

# else 

  big_cmn_sgl_t			*sgl;  
  uint32_t		is_nonld_or_iocmd;
  uint8_t		dma_dir;

  is_nonld_or_iocmd	= rcl_is_nonld_or_iocmd(lxadp->adp, rclp);
  get_dma_direction( rclp, &dma_dir );

  /* Check if pci mapping is done */
  if (rclp->bkup_sge_count) {		
    sgl	= &rclp->cmn_sgl;
    if(scp->use_sg) {
#ifdef __VMKERNEL_MODULE__
      if ( rcl_is_r5rw_or_noniocmd(lxadp->adp, rclp)) {
	uint8_t i;
	for (i = 0; i < scp->use_sg; i++)
	  vmk_phys_to_kmap_free((void *)(sgl->sge[i].vaddr));
      }
#else
      if (is_nonld_or_iocmd) {
	pci_unmap_sg(lxadp->pdev, (struct scatterlist *)scp->request_buffer, rclp->bkup_sge_count, dma_dir);
      }
#endif
    }
    else {
#ifdef __VMKERNEL_MODULE__
      if ( rcl_is_r5rw_or_noniocmd(lxadp->adp, rclp)) {
	vmk_phys_to_kmap_free((void *)(sgl->sge[0].vaddr));
      }
#else
      if (is_nonld_or_iocmd) {
	pci_unmap_single(lxadp->pdev, (dma_addr_t)((addr_t)sgl->sge[0].paddr), scp->request_bufflen, dma_dir);
      }
#endif
    }
  }
  return ;
#endif //>= KERNEL_VERSION(2,6,27)

}

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)

void osl_unwind_kmap( struct scsi_cmnd *scp, rcl_packet_public_t *rclp )
{
	struct scatterlist *os_sgl;
	unsigned int j;
	uint8_t sg_cnt;
  
#ifdef LSI_RAID5_SUPPORT
	big_cmn_sgl_t				*sgl;
#else
	cmn_sgl_t				*sgl;
#endif  

	sgl		= &rclp->cmn_sgl;
	sg_cnt = rclp->bkup_sge_count;

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)
	scsi_for_each_sg(scp, os_sgl, sg_cnt, j)
		if (sgl->sge[j].vaddr) 
			kunmap( sg_page(os_sgl) );
  
#else
  
	os_sgl = (struct scatterlist *)scp->request_buffer;
	for (j = 0; j < sg_cnt; j++) 
		if (sgl->sge[j].vaddr) 
			kunmap(os_sgl[j].page);
#endif
    
}

void osl_unwind_kmap_and_schedule( struct scsi_cmnd * scp, struct megasr_clist *clist, rcl_packet_public_t *rclp, linux_adp_t *linux_adp)
{
	unsigned long			flags;

	osl_unwind_kmap( scp, rclp);

	// schedule ourselves again, to retry mapping
	spin_lock_irqsave(linux_adp->host_lock, flags);
	megasr_clist_merge(clist, &linux_adp->build_io_list_in);
	spin_unlock_irqrestore(linux_adp->host_lock, flags);
    
	schedule_work(&linux_adp->work_build_io_in);
}

/**
 * @brief
 * megasr_linux_build_io
 * The "sleepy" build IO routine. This routine traverses the command in it's dedicated
 * queue and try to obtain the kernel virtual addresses for each IO packet found. We move
 * all such packets to our private list so that they can be processed at their own pace.
 * This is a tasklet, Linux guarantees, we are not running on more than one CPU at the
 * same time.
 **/
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)
static void megasr_linux_build_io(struct work_struct  *work)
{
  linux_adp_t				*linux_adp = container_of(work, linux_adp_t, work_build_io_in);
#else
static void megasr_linux_build_io(void *devp)
{
	linux_adp_t				*linux_adp = (linux_adp_t *) devp;
#endif
	rcl_packet_public_t			*rclp;
	rcl_packet_public_t			*rclp_tmp;
	rcl_packet_public_t			*rclp_split;
	struct scsi_cmnd		*scp;
	big_cmn_sgl_t				*sgl;
	unsigned long			flags;
	struct scatterlist		*mlsgl;
	struct megasr_clist	clist;
	int						i = 0;
	struct page	*pg;
	unsigned int	pg_offset;

	megasr_clist_setup(&clist);

	spin_lock_irqsave(linux_adp->host_lock, flags);

	megasr_clist_merge(&linux_adp->build_io_list_in, &clist);

	spin_unlock_irqrestore(linux_adp->host_lock, flags);

	megasr_clist_traverse_each(rclp, rclp_tmp, &clist, rcl_packet_public_t, list) {

		// now try to get the virtual address of the the nodes in the scatter-gather
		// list
		scp		= (struct scsi_cmnd *)rclp->os_packet;
		sgl		= &rclp->cmn_sgl;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)
		scsi_for_each_sg(scp, mlsgl, sgl->sge_count, i){
		  pg = sg_page(mlsgl);
		  pg_offset = mlsgl->offset;
#else
		mlsgl	= (struct scatterlist *)scp->request_buffer;
		for (i = 0; i < sgl->sge_count; i++) {
		  pg =  mlsgl[i].page;
		  pg_offset = mlsgl[i].offset;
#endif
		  sgl->sge[i].vaddr	= kmap(pg) + pg_offset;

			if (!sgl->sge[i].vaddr) {
				con_log(CL_ERR, ("megaswr[osl]: kmap failed, it's not supposed to [%d, %d]!\n", sgl->sge_count, i));
				osl_unwind_kmap_and_schedule( scp, &clist, rclp, linux_adp );
				return;
			}
		}

		// Successfully mapped all pages to virtual address. Remove the RCL packet from the build IO queue
		// and transfer to RCL layer
		megasr_clist_detach(&rclp->list);
		
		spin_lock_irqsave(linux_adp->host_lock, flags);

		if (!linux_adp->crashdump_mode) {
			osl_cmn_process_rclp(linux_adp->adp, rclp);
		}
		else {
			// We have to split the command if it spills to different strips
			if ((IS_RW(rclp->cmd.u.scsi_packet.u.lsi_cdb) == LSI_FALSE) || !megasr_lib_is_strip_overflow(rclp)) {
				// IO contained in a single strip
				osl_cmn_process_rclp(linux_adp->adp, rclp);
			}
			else {
				// slice this command to smaller chunks
				// In order to do this, we allocate a surrogate raid packet, which will carry the partial operations for us
				if (linux_adp->rclp_crash_inuse) {
					con_log(CL_ERR, ("megasr: unexpected result from crash rcl alloc, only one outstanding command is allowed.\n"));
					osl_unwind_kmap_and_schedule( scp, &clist, rclp, linux_adp );
					return;
				}
				else {
					rclp_split			= linux_adp->rclp_crash;
					linux_adp->rclp_crash_inuse	= 1;
					rclp_split->owner		= LIN_OSL_RCL_ALLOC3;
					rclp_split->status		= STS_SUCCESS;
					lin_set_rclp_ld_ptr(linux_adp->adp, rclp_split, &rclp->device_id);
					megasr_linux_process_low_mem_io(linux_adp, rclp, rclp_split);
				}
			}
		}

		spin_unlock_irqrestore(linux_adp->host_lock, flags);
	}
}

/**
 * @brief
 * megasr_linux_dpc_complete_cmd
 *
 * counterpart for the "sleepy" build IO routine. We complete the scsi commands to the mid-layer
 * These commands are those for which virtual mapping, using kmap(), was obtained.
 **/
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)
static void megasr_linux_dpc_complete_cmd(struct work_struct  *work)
{
  linux_adp_t				*linux_adp = container_of(work, linux_adp_t, work_build_io_out);
#else
static void megasr_linux_dpc_complete_cmd(void *devp)
{
	linux_adp_t				*linux_adp = (linux_adp_t *)devp;
#endif
	rcl_packet_public_t			*rclp;
	rcl_packet_public_t			*rclp_tmp;
	struct scsi_cmnd		*scp;
	big_cmn_sgl_t				*sgl;
	unsigned long			flags;
	struct megasr_clist	clist;
	

	megasr_clist_setup(&clist);

	spin_lock_irqsave(linux_adp->host_lock, flags);

	megasr_clist_merge(&linux_adp->build_io_list_out, &clist);

	spin_unlock_irqrestore(linux_adp->host_lock, flags);

	megasr_clist_traverse_each(rclp, rclp_tmp, &clist, rcl_packet_public_t, list) {

		scp		= (struct scsi_cmnd *)rclp->os_packet;
		sgl		= &rclp->cmn_sgl;

		osl_unwind_kmap( scp, rclp );

		megasr_clist_detach(&rclp->list);
			
		spin_lock_irqsave(linux_adp->host_lock, flags);
		
		linux_unmap_sg ( linux_adp, rclp );

		scp->host_scribble = NULL;
		scp->scsi_done(scp);

		rcl_free_rcl_packet(linux_adp->adp, rclp);
			
		spin_unlock_irqrestore(linux_adp->host_lock, flags);
	}

	spin_lock_irqsave(linux_adp->host_lock, flags);

	/*
	 * Before exiting DPC, check if the work_build_io_out list has any more commands for us to process.
	 * If there are, then reschedule ourselves; reset the dpc_schedule flag otherwise
	 */
	if (!megasr_clist_is_empty(&linux_adp->build_io_list_out)) {
		linux_adp->dpc_scheduled = 1;
		schedule_work(&linux_adp->work_build_io_out);
	}
	else
		linux_adp->dpc_scheduled = 0;

	spin_unlock_irqrestore(linux_adp->host_lock, flags);

}

/**
 *@brief
 * LSI MegaSR slave configure
 * In 2.6 kernels this is called after a device responds to INQUIRY. DVD-RAM devices cannot respond to MODE SENSE 6.
 * In this entry point we will force the SCSI mid-layer to issue MODE SENSE 10.
 */
int lsraid_slave_configure(struct scsi_device *sdev)
{
	/*
	 * Set these parameters only for physical devices
	 */
	if (sdev->channel < LD_BUS ) {
		sdev->use_10_for_ms	= 1;
		sdev->use_10_for_rw	= 1;
	}

	return 0;
}
#endif


/**
 *@brief
 * LSI MegaSR queue command 
 * This function is called by SCSI midlayer when when a new IO command
 * is available for the driver.
 * 
 *@param	scp		scsi command
 *@param	done	call back routine
 *@return	0		If the command successfully passed to the driver
 *@return	1		If the driver busy
 */

#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,0,0)
int lsraid_queue_cmd_lck(struct scsi_cmnd* scp, void (* done)(struct scsi_cmnd *))
#else
int lsraid_queue_cmd(struct scsi_cmnd* scp, void (* done)(struct scsi_cmnd *))
#endif
{
	rcl_packet_public_t	*rclp;
	linux_adp_t		*lxadp = (linux_adp_t *)LSI_RAID_SCP2_HOSTDATA(scp);
	u32 retval = LSI_TRUE;

/* Without lock, it's well tested with 2.6.18 */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19)
	unsigned long	flags;
	spin_lock_irqsave(lxadp->host_lock, flags);
#endif

	scp->scsi_done	= done;
#ifdef __VMKERNEL_MODULE__
	// ESX needs success value from these three commands
	switch(scp->cmnd[0]) {
		case VPD_SERIAL_NUMBER:
		case LSI_SCSIOP_RESERVE_UNIT:
		case LSI_SCSIOP_RELEASE_UNIT:
			scp->result = 0;
			scp->scsi_done(scp);
			return 0;
	}
#endif
    /* If RCl Layer is busy. Notify SCSI Layer to retry the request */
	if((retval = rcl_is_driver_busy(lxadp->adp)) == LSI_TRUE){
		rclp	= rcl_alloc_rcl_packet(lxadp->adp);
	    if (!rclp) {
			retval = LSI_FALSE;
		}else{
		    rclp->owner = LIN_OSL_RCL_ALLOC1;

	        if (scmd_to_rclpkt(scp, rclp, lxadp->pdev) == LSI_TRUE) {
				/* If the LD is Busy. The Request has to be returned back to OS.
				 * Eg.Case: FW download.
				 */ 
				if((retval = lin_is_ld_busy(rclp)) == LSI_TRUE){
					osl_cmn_process_rclp(lxadp->adp, rclp);
	            }else{
	            	rcl_free_rcl_packet(lxadp->adp,rclp);
					retval = SCSI_MLQUEUE_HOST_BUSY;
	            }
		    }
	    }
	} else {
		/* Adapter command block is set */
		retval = SCSI_MLQUEUE_HOST_BUSY;
	}

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19)
	spin_unlock_irqrestore(lxadp->host_lock, flags);
#endif
	return retval;
}

#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,0,0)
DEF_SCSI_QCMD(lsraid_queue_cmd);
#endif

/**
 *@brief
 * LSI MegaSWR abort SCSI command. 
 * SCSI midlayer call this function to abort a SCSI command previously sent to
 * the driver. This event happens if the driver fails to finish the command
 * with in SCSI midlayer scsi command time out period.
 * 
 *@param	scp		Pointer to the Scsi Command structure
 *@return	SCSI ABORT status values defined by the SCSI midlayer	 
 *@remarks
 * This is a driver entry function 
 */
int lsraid_abort(struct scsi_cmnd* scp)
{
	int				ret_val = LSI_TRUE;
	linux_adp_t		*linux_adp = ((linux_adp_t *)LSI_RAID_SCP2_HOSTDATA(scp));
	rcl_packet_public_t	*rclp = (rcl_packet_public_t *)scp->host_scribble;
	rcl_packet_public_t	*rclp_tmp;
	rcl_packet_public_t	*rclp_tmp1;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)
	unsigned long	flags;

	spin_lock_irqsave(linux_adp->host_lock, flags);
#endif

	if (!rclp)
		goto found_null_rclp;

	con_log(CL_ANN, ("megasr[osl]: abort called cmd=[0x%x], rclp[0x%lx]\n", rclp->cmd.u.scsi_packet.u.lsi_cdb[0], (addr_t)rclp));

	ret_val = osl_cmn_abort_cmd(linux_adp->adp, rclp);
	
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
	/* if command aborted successfully, it can be in out bound queue for kunmap process,
	 * Abort function should wait till the command completes kunmap operation.
	 */
	if (ret_val == LSI_TRUE) {
		
		uint8_t	cmd_active_flag;
		
		do {
			cmd_active_flag = 0;
			
			/* find out if this command is present in out bound queue */
			megasr_clist_traverse_each(rclp_tmp, rclp_tmp1, &linux_adp->build_io_list_out, rcl_packet_public_t, list) {
				/* check if command is present in out bound queue */
				if (rclp_tmp == rclp) {
					cmd_active_flag = 1;
					break;
				}
			}	
			
			/* wait if the command is in out bound queue */
			if (cmd_active_flag) {
				spin_unlock_irq(linux_adp->host_lock);
				/* let the system function, dont dead lock */
				msleep(100);
				spin_lock_irq(linux_adp->host_lock);
			}

		} while(cmd_active_flag);
	}
#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) */

	con_log(CL_ANN, ("megasr[osl]: abort over cmd=[0x%x], rclp[0x%lx]\n", rclp->cmd.u.scsi_packet.u.lsi_cdb[0], (addr_t)rclp));
found_null_rclp:

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)
	spin_unlock_irqrestore(linux_adp->host_lock, flags);
#endif

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
	return ((LSI_TRUE == ret_val) ? SUCCESS: FAILED);
#else
	return ((LSI_TRUE == ret_val) ? SCSI_ABORT_SUCCESS: SCSI_ABORT_SNOOZE);
#endif
}

/**
 *@brief
 * LSI MegaSWR device reset
 * This function is called by the SCSI mid layer if abort command failed.
 * 
 *@param	scp		Pointer to the Scsi Command structure
 *@return	SCSI RESET status values defined by the SCSI midlayer	 
 *@remarks
 * This is a driver entry function 
 */
int lsraid_device_reset(struct scsi_cmnd* scp)
{
	device_id_t	device_id;
	uint32_t		ret_val;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)
	linux_adp_t		*lx_adp = ((linux_adp_t *)LSI_RAID_SCP2_HOSTDATA(scp));
	unsigned long	flags;

	spin_lock_irqsave(lx_adp->host_lock, flags);
#endif

	device_id.path		= 0;
	device_id.bus		= LSI_RAID_SCP2_CHANNEL(scp);
	device_id.target	= LSI_RAID_SCP2_TARGET(scp);
	device_id.lun		= LSI_RAID_SCP2_LUN(scp);

	/* Cleanup OS stuff for reset condition and pass the request down to the
	 * next layer */
	ret_val = osl_cmn_reset(((linux_adp_t *)LSI_RAID_SCP2_HOSTDATA(scp))->adp, DEV_RESET, &device_id);

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)
	spin_unlock_irqrestore(lx_adp->host_lock, flags);
#endif

	return ((LSI_TRUE == ret_val) ? SUCCESS: FAILED);
}

/**
 *@brief
 * LSI MegaSWR bus reset
 * This function is called by the SCSI mid layer if reset device failed.
 *
 *@param	scp		Pointer to the Scsi Command structure
 *@return	SCSI RESET status values defined by the SCSI midlayer	 
 *@remarks
 * This is a driver entry function 
 */
int lsraid_bus_reset(struct scsi_cmnd* scp)
{
	device_id_t	device_id;
	uint32_t		ret_val;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)
	linux_adp_t		*lx_adp = ((linux_adp_t *)LSI_RAID_SCP2_HOSTDATA(scp));
	unsigned long	flags;

	spin_lock_irqsave(lx_adp->host_lock, flags);
#endif

	device_id.path		= 0;
	device_id.bus		= LSI_RAID_SCP2_CHANNEL(scp);
	device_id.target	= LSI_RAID_SCP2_TARGET(scp);
	device_id.lun		= LSI_RAID_SCP2_LUN(scp);

	/* Cleanup OS stuff for reset condition and pass the request down to the
	 * next layer */
	ret_val = osl_cmn_reset(((linux_adp_t *)LSI_RAID_SCP2_HOSTDATA(scp))->adp, BUS_RESET, &device_id);

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)
	spin_unlock_irqrestore(lx_adp->host_lock, flags);
#endif

	return ((LSI_TRUE == ret_val) ? SUCCESS: FAILED);
}

/**
 *@brief
 * LSI MegaSWR host reset
 * This function is called by the SCSI mid layer if reset bus failed.
 *
 *@param	scp		Pointer to the Scsi Command structure
 *@return	SCSI RESET status values defined by the SCSI midlayer	 
 *@remarks
 * This is a driver entry function 
 */
int lsraid_host_reset(struct scsi_cmnd* scp)
{
	device_id_t	device_id;
	uint32_t		ret_val;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)
	linux_adp_t		*lx_adp = ((linux_adp_t *)LSI_RAID_SCP2_HOSTDATA(scp));
	unsigned long	flags;

	spin_lock_irqsave(lx_adp->host_lock, flags);
#endif

	device_id.path		= 0;
	device_id.bus		= LSI_RAID_SCP2_CHANNEL(scp);
	device_id.target	= LSI_RAID_SCP2_TARGET(scp);
	device_id.lun		= LSI_RAID_SCP2_LUN(scp);

	/* Cleanup OS stuff for reset condition and pass the request down to the
	 * next layer */
	ret_val = osl_cmn_reset(((linux_adp_t *)LSI_RAID_SCP2_HOSTDATA(scp))->adp, ADP_RESET, &device_id);

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)
	spin_unlock_irqrestore(lx_adp->host_lock, flags);
#endif

	return ((LSI_TRUE == ret_val) ? SUCCESS: FAILED);
}


/**
 *@brief
 * LSI MegaSWR Interrupt Service Routine 
 * This function is called by the SCSI Midlayer if an ISR event happen.
 * 
 *@param irq	IRQ number
 *@param os_cxt	: Driver extension data structure pointer
 *@param regs	: pt_regs structure pointer 
 *@return None
 *@remarks
 * This is a driver entry function 
 */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19)
static LSI_RAID_IRQRETURN_T lsraid_isr(int irq, void* os_cxt )
#else
static LSI_RAID_IRQRETURN_T lsraid_isr(int irq, void* os_cxt, struct pt_regs* regs)
#endif
{
	int				ret_val = LSI_FALSE;
	unsigned long	flags;
	linux_adp_t		*lx_adp	= os_cxt;

	spin_lock_irqsave(lx_adp->host_lock, flags);

	ret_val = megasr_lib_isr(lx_adp->adp, 0);

	/* @todo should be done from here and needs to be removed from success failure handler */
	osl_cmn_flush_schedule_queue(lx_adp->adp);

	spin_unlock_irqrestore(lx_adp->host_lock, flags);

	if (ret_val == LSI_TRUE) {
		return IRQ_RETVAL(1);
	} else {
		return IRQ_RETVAL(0);
	}
}

#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,0,0)
static LSI_RAID_IRQRETURN_T lsraid_msix_isr(int vector, void* os_cxt )
{
	int				ret_val = LSI_FALSE;
	unsigned long	flags;
	linux_adp_t		*lx_adp	= os_cxt;
        
	spin_lock_irqsave(lx_adp->host_lock, flags);
	if (vector == lx_adp->msix_intr[0].vector) {
		ret_val = megasr_lib_isr(lx_adp->adp, 1);
	}
	else {
		ret_val = megasr_lib_isr(lx_adp->adp, 2);
	}

	osl_cmn_flush_schedule_queue(lx_adp->adp);

	spin_unlock_irqrestore(lx_adp->host_lock, flags);

	if (ret_val == LSI_TRUE) {
		return IRQ_RETVAL(1);
	} else {
		return IRQ_RETVAL(0);
	}
}

static LSI_RAID_IRQRETURN_T lsraid_err_isr(int vector, void* os_cxt )
{
	linux_adp_t	*lxadp	= os_cxt;

	if (vector == lxadp->msix_intr[1].vector) {
		con_log(CL_WARN, ("megasr[osl]: MSIX lsraid_err_isr0: %d\n", vector));
	}
	else {
		con_log(CL_WARN, ("megasr[osl]: MSIX lsraid_err_isr1: %d\n", vector));
	}
	return lsraid_isr (vector, os_cxt);	
}
#endif

#ifdef __VMKERNEL_MODULE__
/*
 * Detect stub to force 2.4 kernels to rescan luns
 */
static int lsraid_detect(Scsi_Host_Template *template)
{
	con_log(CL_ERR, ("megasr[osl]: returning %d from lsiraid_detect\n", adp_count_g));
   return adp_count_g;
}
#endif

static void megasr_pci_enable_intx(struct pci_dev *pdev)
{
	u16       pci_command;

	pci_read_config_word(pdev, PCI_COMMAND, &pci_command);

	if (pci_command & PCI_COMMAND_INTX_DISABLE) {
		pci_command &= ~PCI_COMMAND_INTX_DISABLE;
		pci_write_config_word(pdev, PCI_COMMAND, pci_command);
	}
}

/**
 *@brief
 * LSI megaswr Initialize adapter 
 * Perform complete initialization for one adapter.
 *
 *@param	pdev	linux specific pci device structure
 *@param	id		Set of pci attributes of this adapter
 *@return	0		If success
 *@return	Non zero scsi error value in case of failure
 *@remarks
 * This function is used by both 2.6 and 2.4 kernel drivers. 
 */
static int __devinit lsraid_probe_one(struct pci_dev* pdev,
		const struct pci_device_id *id)
{
	pvt_data_t			*adp	= NULL;
	linux_adp_t			*lxadp	= NULL;
	struct Scsi_Host	*host	= NULL;
	uint32_t			i		= 0;
	uint32_t			status	= 0;

	for (i = 0; i < adp_count_g ; i++) {
	
		lxadp = (linux_adp_t*)lin_get_linux_adp(adps_g[i]); 
		
		if ((lxadp->pdev->bus->number == pdev->bus->number) && 
				(lxadp->pdev->devfn == pdev->devfn)) {
			con_log(CL_ERR,("megaswr[osl]:  reject probe request for dev\n"));

			con_log(CL_ERR,("megaswr[osl]: %#4.04x:%#4.04x:%#4.04x:%#4.04x:",
				pdev->vendor, pdev->device, pdev->subsystem_vendor,
				pdev->subsystem_device));

			return -ENODEV;
		}
	}

	// Announce PCI information for the probed controller
	con_log(CL_ANN, ("megasr: [RAID HBA] %#4.04x:%#4.04x:%#4.04x:%#4.04x: bus %d:slot %d:func %d\n",
		pdev->vendor, pdev->device, pdev->subsystem_vendor, pdev->subsystem_device,
		pdev->bus->number, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn)));

	if(pci_enable_device(pdev))
		return -ENODEV;

	pci_set_master(pdev);

	// Get the size of adapter structure
	// allocate memory, zero out
	// link Linux adapter and adp
	adp = kmalloc(megasr_lib_get_adp_size(), GFP_KERNEL);
	if ( !adp )
		return -ENODEV;
	
	memset(adp, 0, megasr_lib_get_adp_size());

	lxadp = kmalloc(sizeof(linux_adp_t), GFP_KERNEL);
	if (!lxadp){
		goto lsraid_fail_step1;
	}

	memset(lxadp, 0, sizeof(linux_adp_t));

	lxadp->rclp_crash	= kmalloc(megasr_lib_get_rcl_packet_size(), GFP_KERNEL);
	if (!lxadp->rclp_crash) {
		goto lsraid_fail_step2;
	}
	memset(lxadp->rclp_crash, 0, megasr_lib_get_rcl_packet_size());

	lxadp->adp = adp;
	megasr_lib_set_lxadp(adp, lxadp);

	lxadp->pdev	= pdev;

	/* Store pci config info in adp */
	extract_pciconfig(pdev, adp);

	if (megasr_init_adp(adp, lxadp->pdev->bus->number, (lxadp->pdev->devfn >> 3), (lxadp->pdev->devfn & 7)) == LSI_FALSE) {
		con_log(CL_WARN,("megaswr[osl]:  rcl_claim_adapter failed\n"));
		goto lsraid_fail_step2;
	}

	if (allocate_static_mem_and_add_to_memmgr(lxadp) != LSI_TRUE) {
		con_log(CL_WARN,("megaswr[osl]:  allocate mem and add to memmgr failed!!\n"));
		goto out_resources_rclp_crash;
	}

	megasr_lib_clear_msix_mode(adp);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,0,0)
	if(megasr_lib_get_adp_type_scu(adp)) {
		megasr_lib_set_msix_mode(adp);
	}
#endif

	if(rcl_init_adapter(adp) == LSI_FALSE) {
		con_log(CL_WARN,("megaswr[osl]:  rcl_init_adapter failed\n"));
		goto out_free_memory_manager;
	}

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
	// setup work queues for DPC
	megasr_clist_setup(&lxadp->build_io_list_in);
	megasr_clist_setup(&lxadp->build_io_list_out);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)
	INIT_WORK(&lxadp->work_build_io_in, megasr_linux_build_io);
	INIT_WORK(&lxadp->work_build_io_out, megasr_linux_dpc_complete_cmd);
#else	
	INIT_WORK(&lxadp->work_build_io_in, megasr_linux_build_io, lxadp);
	INIT_WORK(&lxadp->work_build_io_out, megasr_linux_dpc_complete_cmd, lxadp);
#endif

#endif
/** initalization of aen read flag **/
megasr_poll_wait_aen = 0;

#ifdef __LP64__
	if (pci_set_dma_mask(pdev, 0xFFFFFFFFFFFFFFFFULL) != 0) {
		con_log(CL_ERR, ("megaswr[osl]: failed to set the DMA mask\n"));

		if (pci_set_dma_mask(pdev, 0xFFFFFFFF) != 0) {
			con_log(CL_ERR, ("megaswr[osl]: failed to set the DMA mask again\n"));
			goto lsraid_fail_step3;
		}
	}
	else {
		con_log(CL_INFO, ("megaswr[osl]: FLAG FFFFFFFFFFFFFFFF set\n"));	
	}
#else
	if (pci_set_dma_mask(pdev, 0xFFFFFFFF) != 0) {
		con_log(CL_ERR, ("megaswr[osl]: failed to set the DMA mask\n"));
		goto lsraid_fail_step3;
	}
#endif
		
#ifdef __VMKERNEL_MODULE__
	host = vmk_scsi_register(lsraid_template_gp, sizeof(linux_adp_t *), pdev->bus->number, pdev->devfn);
#else
	host = LSI_RAID_SCSI_REGISTER(lsraid_template_gp, sizeof(linux_adp_t *));
#endif

	if(!host) {
		con_log(CL_ERR, ("megaswr[osl]:  scsi_register failed\n"));
		goto lsraid_fail_step3;
	}

	/* to make sure the SCSI Mid Layer IOCTL Works */
	LSI_RAID_SCSI_SET_PCI_DEVICE(host, pdev);

#ifdef __VMKERNEL_MODULE__
	#error " vmkernel"
	lxadp->host_lock	= &io_request_lock;
#else
	/* Init Global Lock */
	spin_lock_init(&lxadp->lock);

	lxadp->host_lock	= &lxadp->lock;

	/* To handle kernels with out host_lock */
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
	LSI_RAID_HOST_SET_LOCK(host, lxadp->host_lock);
#endif

#endif

	host->hostdata[0] = (unsigned long) lxadp;

	lxadp->host		= host;

	host->this_id		= -1;
	host->unique_id		= pdev->bus->number << 8 | pdev->devfn;
#ifdef __VMKERNEL_MODULE__
	host->max_id		= MAX_LD_PER_ADP * (LD_BUS + 1);
	host->max_channel	= 0;
#else
	host->max_id		= MAX_LD_PER_ADP;
	host->max_lun		= 1;
	host->max_channel	= LD_BUS;
#endif
	host->max_lun		= 1;
	host->base			= pci_resource_start(pdev, 1);
	host->irq			= pdev->irq;
	host->max_cmd_len	= MAX_CDB_LENGTH;
	megasr_lib_init_adp(adp, &host->cmd_per_lun, &host->max_sectors, adp_count_g);
	if (!lxadp->crashdump_mode) {
		host->can_queue		= MAX_IO_PER_ADP;
		host->sg_tablesize	= MAX_SGE_COUNT_FOR_OS;
	}
	else {
		host->can_queue		= 1;
		host->max_sectors	= lxadp->crashdump_sectors;
		host->sg_tablesize	= MAX_SGE_COUNT_FOR_OS_HIBER;
	}
	
	megasr_lib_disable_intr(lxadp->adp);

	if (rcl_scan_devices(adp) == LSI_FALSE) {
		con_log(CL_ERR,("megaswr[osl]:  rcl scan devices failed\n"));
		goto lsraid_fail_init;
	}
		
	/* setup adapter handle in PCI soft state */
	pci_set_drvdata(pdev, host);

	/* set up ioctl interface for this adapter */
	if (lsraid_load_ioctl_module(adp) == LSI_FALSE) {
		con_log(CL_ERR,("megaswr[osl]:  load ioctl module failed\n"));
		goto lsraid_fail_init;
	}

	/* configuration load function call */
	if (rcl_read_and_populate_cfg(adp) == LSI_FALSE) {
		con_log(CL_ERR,("megaswr[osl]:  configuration resolve failed\n"));
		goto lsraid_fail_init;
	}
	
	if(megasr_lib_get_msix_mode(lxadp->adp)){
		status = megasr_msix_enable (lxadp, pdev);
	}
	else {
		megasr_pci_enable_intx(pdev); // Not sure if this is required
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)
		status = request_irq(pdev->irq, lsraid_isr, IRQF_SHARED, "megasr", lxadp);
#else
		status = request_irq(pdev->irq, lsraid_isr, SA_SHIRQ, "megasr", lxadp); 
#endif
	}
	if (status){
		con_log(CL_ERR,("megaswr[osl]: request_irq failed: %d\n", status));
		goto lsraid_fail_init;
	}

	megasr_lib_enable_intr(lxadp->adp);

	/* raid key authentication call */
	rcl_authenticate_ibutton(adp);

	if (LSI_RAID_SCSI_ADD_HOST(host, &pdev->dev)) {
		con_log(CL_ERR,("megaswr[osl]:  scsi add host failed\n"));
		goto lsraid_free_msix;
	}

#ifndef __VMKERNEL_MODULE__
	LSI_RAID_SCSI_SCAN_HOST(host);
#else
	vmk_scsi_register_uinfo(host, pdev->bus->number, pdev->devfn, (void*)adp);
#endif
	
	adp_count_g++;

#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
	if(megasas_proc_hba_map)
		megasas_create_proc_entry(lxadp);
#endif
	return 0;

lsraid_free_msix:
	megasr_free_irq(lxadp, pdev);

lsraid_fail_init:
	LSI_RAID_SCSI_HOST_DEALLOC(host);

lsraid_fail_step3:
	rcl_unload(adp);

	osl_destroy_all_timers(lxadp);

out_free_memory_manager:
	remove_mem_allocated_for_memmgr(lxadp);

out_resources_rclp_crash:
	kfree(lxadp->rclp_crash);

lsraid_fail_step2:
	kfree((void *)lxadp);

lsraid_fail_step1:
	kfree(adp);

	return -ENODEV;
}

/**
 *@brief
 * LSI megaswr Detach one adapter 
 * Perform complete unloading of driver for one adapter
 *
 *@param	dev		linux specific pci device structure
 *@return	None 
 *@remarks
 * This function is used by both 2.6 and 2.4 kernel drivers. 
 */
static void lsraid_detach_one(struct pci_dev *dev)
{
	struct Scsi_Host	*host = pci_get_drvdata(dev);
	linux_adp_t			*lxadp	= (linux_adp_t *)host->hostdata[0];
    unsigned long       flags;

	con_log(CL_OSL, ("megaswr[osl]: driver detach function called\n"));

	/* shut down all active operations, and prepare for system shut down */
    spin_lock_irqsave(lxadp->host_lock, flags);
	rcl_shutdown(lxadp->adp);
    spin_unlock_irqrestore(lxadp->host_lock, flags);
	
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
	
	if (megasas_proc_root && lxadp->hba_proc_dir) {
		megasas_remove_proc_entry(lxadp);
	}

	if (adp_count_g == 1) {
		if (megasas_proc_root) {
			remove_proc_entry("version", megasas_proc_root);
			remove_proc_entry("release_date", megasas_proc_root);
			remove_proc_entry("hba_map", megasas_proc_root);
			remove_proc_entry(LSIRAID_DRIVER_SIGNATURE, proc_scsi);
		}
	}
#endif
	con_log(CL_DEBUG, ("megaswr[osl]:  Releasing the controller... "));

	osl_destroy_all_timers(lxadp);

	LSI_RAID_SCSI_REMOVE_HOST(host);
	
	megasr_free_irq(lxadp, lxadp->pdev);
	
	rcl_unload(lxadp->adp);

	remove_mem_allocated_for_memmgr(lxadp);
	
	if (adp_count_g == 1) {
		lsraid_unload_ioctl_module(lxadp);
	}

	kfree(lxadp->rclp_crash);

	kfree(lxadp->adp);

	kfree(lxadp);

	LSI_RAID_SCSI_HOST_DEALLOC(host);
	
	adp_count_g--;
}

/**
 *@brief
 * Extract PCI configuration data
 * This function is used to extract PCI configuration data from pci_dev
 * structure to driver provided structure pci_config_space.
 * 
 *@param pci_dev : linux specific pci device structure
 *@param pci_cfg : swraid specific pci device structure
 *@return None
 */
static void extract_pciconfig(struct pci_dev* pdev, pvt_data_t* adp)
{
	int			i;
	uint32_t	cfg_size = 0;
	uint8_t		*ptr;

	ptr = lin_get_adp_pci_config_space_ptr_and_size(adp, &cfg_size);

	for(i = 0; i < cfg_size; ptr++, i++)
		pci_read_config_byte(pdev, i, ptr);

	for(i = 0; i < MAX_PCI_ELEMENTS; ptr++, i++)
		lin_set_adp_pci_config(pci_resource_start(pdev,i),i, adp);

	return;
}

/**
 *@brief
 * This is used to de-allocate the memory allocated for memory manager.
 */
void remove_mem_allocated_for_memmgr(linux_adp_t *lxadp)
{
	while(lxadp->dma.list_count) {
		lxadp->dma.list_count--;
		megasr_dma_free(lxadp->pdev, &lxadp->dma.list[lxadp->dma.list_count]);
	}
}

/**
 *@brief
 * This is used to allocate the required memory for linux driver and set up
 * the memory manager. Adjustments of required memory is done inside this
 * function. Fail condition of this function is fully implemented. 
 */
boolean_t allocate_static_mem_and_add_to_memmgr(linux_adp_t *lxadp)
{
	pvt_data_t *adp		= lxadp->adp;
	linux_dma_mem_t		*dma = &(lxadp->dma);
	uint32_t 			try_size;
	/* static dma pool size, windows driver allocates TOTAL_STATIC_MEM_REQ	*/
	uint32_t			static_pool_len = TOTAL_STATIC_MEM_REQ;
	uint32_t			static_dma_pool_len = 2 * LSI_RAID_1_MB_SIZE;
	cmn_sgl_t			dyn_sgl;
	uint32_t			crashdump_dynamic_mem_size;
	void				*crashdump_ddf_mem_va;
	unsigned long			crashdump_ddf_mem_dma;
	uint8_t				alloc_flag = LINUX_DMA_NOWAIT;

	if ( megasr_get_adp_type(adp)){
		static_pool_len = (2*TOTAL_STATIC_MEM_REQ + LSI_RAID_1_MB_SIZE);
	}
	else {
		static_pool_len = TOTAL_STATIC_MEM_REQ;
	}
	con_log(CL_INFO, ("megasr[osl]:  static memory allocated :%d\n",static_pool_len));
	mem_mgr_init_mem_mgr(adp, MEGASR_STRIPE_SIZE_64K); // Initialize with default 64k node size, this can be overridden by crashdump driver.
	lxadp->static_mem = NULL;
	dma->list_count = 0;

	if (megasr_dma_alloc(lxadp->pdev, LSI_RAID_1_MB_SIZE, &dma->list[dma->list_count], LINUX_DMA_NOWAIT) != LSI_TRUE) {
		con_log(CL_ERR, ("megasr[osl]:  out of memory in %s for VA only pool\n", __FUNCTION__));
		goto lsraid_fail_step1;
	}
	
	if (LSI_TRUE != mem_mgr_add_to_mem_pool(adp, dma->list[dma->list_count].vir,	NULL, dma->list[dma->list_count].len, NO_PHY_ADDR_MEM_POOL)) {
		con_log(CL_ERR, ("megasr[osl]:  add static memory to the memory pool failed\n"));
		goto lsraid_fail_step1;
	}

	dma->list_count++;

	try_size = static_pool_len;
	while (static_pool_len) {

		if (dma->list_count >= MAX_MEM_POOLS_PER_POOL_TYPE) {
			con_log(CL_ERR, ("megasr[osl]:  memory allocation failed, overflow\n"));
			goto lsraid_fail_step1;
		}

		if (megasr_dma_alloc(lxadp->pdev, try_size, &dma->list[dma->list_count], LINUX_DMA_NOWAIT) != LSI_TRUE) {
			try_size /= 2;
			if (try_size < (LSI_RAID_1_MB_SIZE / 8)) {
				/* i cannot try any more and giving up */
				con_log(CL_ERR, ("megasr[osl]:  warning, driver allocated %#x bytes less than requested memory\n", static_pool_len));
				return LSI_TRUE;
			}
			continue;
		}

		if (LSI_TRUE != mem_mgr_add_to_mem_pool(adp, dma->list[dma->list_count].vir, (uint8_t *)(addr_t)dma->list[dma->list_count].dma_addr, try_size, STATIC_MEM_POOL)) {
				con_log(CL_ERR, ("megasr[osl]:  add memory to the memory pool failed\n"));
				goto lsraid_fail_step1;
		}
	
		static_pool_len -= try_size; 
		dma->list_count++;
	}

	// Get memory for initial dynamic buffers
	try_size		= static_dma_pool_len;
	lxadp->crashdump_mode	= 0;
	for (;;) {
		if (megasr_dma_alloc(lxadp->pdev, try_size, &dma->list[dma->list_count], alloc_flag) != LSI_TRUE) {

			if (lxadp->crashdump_mode) {
				// Memory allocation failed for minimum memory required (e.g., in crashdump mode), give-up
				con_log(CL_ERR, ("megasr[linux]: failed to allocate minimum dynmaic pool for low memory conditions.\n"));
				break;
			}
			else {
				try_size /= 2;

				if (try_size <= MEGASR_LINUX_CRASHDUMP_MEMORY) {
					// We must get at least memory.
					con_log(CL_WARN, ("megasr[linux]: low memory condition detected in the driver.\n"));
					lxadp->crashdump_sectors	= MEGASR_LINUX_CRASHDUMP_SECTORS;
					try_size			= MEGASR_LINUX_CRASHDUMP_MEMORY;
					lxadp->crashdump_mode		= 1;

				}
			}
		}
		else {
			// For crashdump cases, we must have at least MEGASR_LINUX_CRASHDUMP_MEMORY memory. While allocation, if we run down
			// to this value, we implicitly assume a low memory condition and configure the driver accordingly.
			oss_memset((uint8_t *)&dyn_sgl, sizeof(cmn_sgl_t), 0);

			if (!lxadp->crashdump_mode) {

				// call the fn to set the desc size in mem mgr
				mem_mgr_get_desc_size_for_sgl_mem(adp, try_size);

				dyn_sgl.sge_count = 1;
				dyn_sgl.sge[0].length = try_size;
				dyn_sgl.sge[0].vaddr = dma->list[dma->list_count].vir;
				dyn_sgl.sge[0].paddr = (uint8_t *)(unsigned long)dma->list[dma->list_count].dma_addr;	// This is broken till we are capable of supported dma >4GB on 32-bit plaforms :-(
				if (mem_mgr_add_sgl_mem(adp, &dyn_sgl, NULL) == LSI_FALSE) {
					con_log(CL_ERR, ("megasr[osl]: can't add mem to sgl pool for static dma\n"));
					goto lsraid_fail_step1;
				}
			}
			else {
				con_log(CL_ANN, ("megasr: Loading in a low memory condition, IO size:[%d] sectors\n", lxadp->crashdump_sectors));
				megasr_lib_set_low_mem_condition(lxadp->adp);
				mem_mgr_override_dynamic_cache_size(adp, lxadp->crashdump_sectors << LSIRAID_SECTOR_SHIFT);

				crashdump_dynamic_mem_size	= try_size - MEGASR_LINUX_CRAHSDUMP_DDF_MEM;
				crashdump_ddf_mem_va		= dma->list[dma->list_count].vir + crashdump_dynamic_mem_size;
				crashdump_ddf_mem_dma		= (unsigned long)dma->list[dma->list_count].dma_addr + crashdump_dynamic_mem_size;

				// call the fn to set the desc size in mem mgr
				mem_mgr_get_desc_size_for_sgl_mem(adp, crashdump_dynamic_mem_size);

				dyn_sgl.sge_count = 1;
				dyn_sgl.sge[0].length = crashdump_dynamic_mem_size;
				dyn_sgl.sge[0].vaddr = dma->list[dma->list_count].vir;
				dyn_sgl.sge[0].paddr = (uint8_t *)(unsigned long)dma->list[dma->list_count].dma_addr;	// This is broken till we are capable of supported dma >4GB on 32-bit plaforms :-(
				if (mem_mgr_add_sgl_mem(adp, &dyn_sgl, NULL) == LSI_FALSE) {
					con_log(CL_ERR, ("megasr[osl]: can't add mem to sgl pool for static dma\n"));
					goto lsraid_fail_step1;
				}

				// Add memory to load DDF for crashdump
				oss_memset((uint8_t *)&dyn_sgl, sizeof(cmn_sgl_t), 0);
				dyn_sgl.sge_count = 1;
				dyn_sgl.sge[0].length = MEGASR_LINUX_CRAHSDUMP_DDF_MEM;
				dyn_sgl.sge[0].vaddr = crashdump_ddf_mem_va;
				dyn_sgl.sge[0].paddr = (uint8_t *)crashdump_ddf_mem_dma;
				if (lsi_mm_add_sgl_mem_for_ddf(adp, &dyn_sgl, 0) == LSI_FALSE) {
						con_log(CL_ERR, ("megasr: can't add mem to sgl pool for DDF load for crashdump\n"));
					goto lsraid_fail_step1;
				}
			}

			dma->list_count++;
			break;
		}
	}

	return LSI_TRUE;

lsraid_fail_step1:
	remove_mem_allocated_for_memmgr(lxadp);

	return LSI_FALSE;
}


static inline void set_start_blk_and_blk_count(struct scsi_cmnd *scp, rcl_packet_public_t *rclp)
{
	scsi_packet_t	*sp;
  sp		= &rclp->cmd.u.scsi_packet;
  sp->cdb_len	= scp->cmd_len;

  switch (sp->cdb_len) {
  case 10 :
    rclp->start_blk = GET_START_BLOCK(sp->u.lsi_cdb); 
    rclp->blk_count = GET_NUMBER_OF_BLOCKS(sp->u.lsi_cdb);
		break;
  case 16:
    rclp->start_blk = GET_START_BLOCK16(sp->u.lsi_cdb); 
    rclp->blk_count = GET_NUMBER_OF_BLOCKS16(sp->u.lsi_cdb);
    break;
  case 6 :
    rclp->start_blk = GET_START_BLOCK6(sp->u.lsi_cdb); 
    rclp->blk_count = GET_NUMBER_OF_BLOCKS6(sp->u.lsi_cdb);
    break;
  case 12 :
    rclp->start_blk = GET_START_BLOCK12(sp->u.lsi_cdb); 
    rclp->blk_count = GET_NUMBER_OF_BLOCKS12(sp->u.lsi_cdb);
    break;
  }

  
  return;
}



static inline void  set_dma_direction(struct scsi_cmnd *scp, rcl_packet_public_t *rclp, uint8_t *dma_dir)
{

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
	if(scp->sc_data_direction == DMA_TO_DEVICE) {
	  *dma_dir				= PCI_DMA_TODEVICE;
		rclp->io_direction	= IODIR_TO_DEVICE;
	} else if(scp->sc_data_direction == DMA_FROM_DEVICE) {
		*dma_dir				= PCI_DMA_FROMDEVICE;
		rclp->io_direction	= IODIR_FROM_DEVICE;
	} else if(scp->sc_data_direction == DMA_NONE) {
		*dma_dir				= PCI_DMA_NONE;
		rclp->io_direction	= IODIR_NONE;
	} else {
		*dma_dir				= PCI_DMA_BIDIRECTIONAL;
		rclp->io_direction	= IODIR_BIDIRECTIONAL;
	}
#else
	if(scp->sc_data_direction == SCSI_DATA_WRITE) {
		*dma_dir				= PCI_DMA_TODEVICE;
		rclp->io_direction	= IODIR_TO_DEVICE;
	} else if(scp->sc_data_direction == SCSI_DATA_READ) {
		*dma_dir				= PCI_DMA_FROMDEVICE;
		rclp->io_direction	= IODIR_FROM_DEVICE;
	} else if(scp->sc_data_direction == SCSI_DATA_NONE) {
		*dma_dir             = PCI_DMA_NONE;
        rclp->io_direction  = IODIR_NONE;
	} else {
		*dma_dir				= PCI_DMA_BIDIRECTIONAL;
		rclp->io_direction	= IODIR_BIDIRECTIONAL;
	}
#endif
}

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)

static inline int setup_sglist_each_sg( struct scsi_cmnd *scp, rcl_packet_public_t *rclp, uint32_t is_nonld_or_iocmd, linux_adp_t *linux_adp )
{
	struct scatterlist	*mlsgl;
	big_cmn_sgl_t		*sgl;
	int			i;
  
	rclp->buffer_len = 0;

	sgl		= &rclp->cmn_sgl;
	sgl->sge_count = scsi_dma_map(scp);
	BUG_ON(sgl->sge_count < 0);	
	BUG_ON(sgl->sge_count > MAX_BIG_SGE_CNT);

	if ( !is_nonld_or_iocmd ){  // For non-IO commands for LD, rcl manager does not expect a multi-node data transfer.
		if(sgl->sge_count > 1){
			con_log(CL_ERR, ("megasr [osl]: unexpected sg nodes count for command:%#x\n", scp->cmnd[0]));
			rclp->status 		= STS_ERROR;
			rclp->scsi_status	= SCSISTAT_GOOD;
			rclp->failure_handler(linux_adp->adp, rclp);
			return LSI_FALSE;
		}
	}
	
	if(sgl->sge_count){
		scsi_for_each_sg(scp, mlsgl, sgl->sge_count, i){
			sgl->sge[i].length = sg_dma_len(mlsgl);
			sgl->sge[i].paddr = (void *)(addr_t)sg_dma_address(mlsgl);
			sgl->sge[i].vaddr = (uint8_t *)(void *)sg_page(mlsgl) + mlsgl->offset;
			rclp->buffer_len	+= sgl->sge[i].length;
		}
	}

	rclp->bkup_sge_count = sgl->sge_count;
	return LSI_TRUE;
  
}
#else
/**
 * @brief
 * Handle zero length  sglist. In the old kernels the scsi_cmnd request buffer holds the buffer address
 * in case of no sglist. 
 * 
 * @param scp	scsi mid layer command
 * @param rclp	rcl packet
 * @param dev	pci_dev
 * @param dma_dir dma direction
 *
 * @return LSI_TRUE on success
 * @return LSI_FALSE on Failure.
 **/
static inline int setup_zero_length_sglist( const struct scsi_cmnd *scp, rcl_packet_public_t *rclp, struct pci_dev *dev, uint8_t dma_dir, uint32_t is_nonld_or_iocmd )
{

	dma_addr_t		dmah;
	big_cmn_sgl_t		*sgl;  

	sgl = &rclp->cmn_sgl;

	if (!scp->use_sg) {
		if (scp->request_bufflen) {
#ifdef __VMKERNEL_MODULE__
			dmah = scp->request_bufferMA;

			if ( rcl_is_r5rw_or_noniocmd(linux_adp->adp, rclp)){
				sgl->sge[0].vaddr	= vmk_phys_to_kmap(scp->request_bufferMA, scp->request_bufflen);
			}
			else {
				sgl->sge[0].vaddr = 0;
			}
#else
			if (is_nonld_or_iocmd) {
				dmah = pci_map_single(dev, scp->request_buffer, scp->request_bufflen, dma_dir);
			}
			else {
				dmah	= 0;
			}
			sgl->sge[0].vaddr	= scp->request_buffer;
#endif

			sgl->sge_count		= 1;
			sgl->sge[0].paddr	= (void *) (addr_t)dmah;
			sgl->sge[0].length	= scp->request_bufflen;
		}
		else {
			sgl->sge_count = 0;
		}

		rclp->bkup_sge_count = sgl->sge_count;
		return LSI_TRUE;
	}
	//  con_log( CL_ERR, ("chenna: Line %d  scp->use_sg = %d \n", __LINE__, scp->use_sg ) );  

	return LSI_FALSE;
}

/**
 * @brief
 * Handle multi length sglist by considering non ld and io command.
 * 
 * @param scp	scsi mid layer command
 * @param rclp	rcl pkt
 * @param dev   pci_dev 
 * @param dma_dir dma direction
 *
 * @return LSI_TRUE on success
 * @return LSI_FALSE on Failure.
 **/
static inline  int setup_multi_length_sglist( const struct scsi_cmnd *scp,rcl_packet_public_t *rclp, struct pci_dev *dev,
					      uint8_t dma_dir,uint32_t is_nonld_or_iocmd,  linux_adp_t	*linux_adp)
{
	struct scatterlist	*mlsgl;
	int			sgcnt;
	big_cmn_sgl_t		*sgl;
	int			i;
  
	sgl		= &rclp->cmn_sgl;
	mlsgl = (struct scatterlist *)scp->request_buffer;
	
#ifdef __VMKERNEL_MODULE__
	sgcnt = scp->use_sg;
#else
	if (is_nonld_or_iocmd) {
		sgcnt = pci_map_sg(dev, mlsgl, scp->use_sg, dma_dir);
	}
	else {
		sgcnt	= scp->use_sg;
	}
#endif

	rclp->buffer_len = 0;

	if (is_nonld_or_iocmd) {
		if (sgcnt > MAX_BIG_SGE_CNT) {
			con_log(CL_ERR, ("megasr [osl]: unexpected sg nodes count for command:%#x - scp->use_sg = %d \n", scp->cmnd[0], scp->use_sg));
			rclp->status 		= STS_ERROR;
			rclp->scsi_status	= SCSISTAT_GOOD;
			rclp->failure_handler(linux_adp->adp, rclp);
			return LSI_FALSE;
		}
		for (i = 0; i < sgcnt; i++) {
			sgl->sge[i].paddr	= (void *)(addr_t)sg_dma_address(&mlsgl[i]);
			sgl->sge[i].length	= sg_dma_len(&mlsgl[i]);
			rclp->buffer_len	+= sgl->sge[i].length;
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
#ifdef __VMKERNEL_MODULE__
			if ( rcl_is_r5rw_or_noniocmd(linux_adp->adp, rclp)){
				sgl->sge[i].vaddr  = vmk_phys_to_kmap(sgl->sge[i].paddr, sgl->sge[i].length);
			}
#else
			sgl->sge[i].vaddr  = mlsgl[i].address;
#endif
#endif
		}
	}
	else {
		// For non-IO commands for LD, rcl manager does not expect a multi-node data transfer.
		if (scp->use_sg != 1) {
			con_log(CL_ERR, ("megasr [osl]: unexpected sg nodes count for command:%#x - scp->use_sg = %d \n", scp->cmnd[0], scp->use_sg));
			rclp->status 		= STS_ERROR;
			rclp->scsi_status	= SCSISTAT_GOOD;
			rclp->failure_handler(linux_adp->adp, rclp);
			return LSI_FALSE;
		}
		sgl->sge[0].paddr	= 0;
		sgl->sge[0].vaddr	= 0;
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
#ifdef __VMKERNEL_MODULE__
		if ( rcl_is_r5rw_or_noniocmd(linux_adp->adp, rclp)){
			sgl->sge[0].vaddr  = vmk_phys_to_kmap(sgl->sge[0].paddr, sgl->sge[0].length);
		}
#else
		sgl->sge[0].vaddr  = mlsgl[0].address;
#endif
#endif
		sgl->sge[0].length	= scp->request_bufflen;
		rclp->buffer_len	= scp->request_bufflen;
	}
	sgl->sge_count = sgcnt;
	rclp->bkup_sge_count = sgl->sge_count;

	return LSI_TRUE;
}
#endif
/**
 * @brief
 * Convert SCSI command to RCL packet
 * This is used to convert scsi cmd to rcl packet command.  We detect if the IO is
 * meant for a RAID 5 volume and if it is a READ/WRITE command. In this case, the
 * tasklet to map the virtual address is scheduled. Otherwise, send the command
 * directly to RCL for execution.
 * 
 * @param scp	scsi mid layer command
 * @param rclp	rcl packet
 * @param dev	linux specific pci device
 *
 * @return LSI_TRUE if this command can be issue right away
 * @return LSI_FALSE if this command is scheduled for tasklet
 **/
static uint32_t scmd_to_rclpkt(struct scsi_cmnd *scp, rcl_packet_public_t *rclp, struct pci_dev *dev)
{
	linux_adp_t		*linux_adp;
	uint8_t			dma_dir;
	uint32_t		rc = LSI_TRUE;
	uint32_t		is_nonld_or_iocmd;
	scsi_packet_t		*sp = &rclp->cmd.u.scsi_packet;

	linux_adp	= (linux_adp_t *)LSI_RAID_SCP2_HOSTDATA(scp);

	scp->host_scribble		= (uint8_t*) rclp;
	rclp->os_packet			= (pvt_data_t *)scp;
	rclp->device_id.path	= 0;
	rclp->device_id.bus		= LSI_RAID_SCP2_CHANNEL(scp);
	rclp->device_id.target	= LSI_RAID_SCP2_TARGET(scp);
	rclp->device_id.lun		= LSI_RAID_SCP2_LUN(scp);
	rclp->cmd.packet_type	= SCSI_PACKET;

	/* both success and failure hanlers are same */
	rclp->success_handler	= (rclp_callback_t)osl_cmd_pkt_success_handler;
	rclp->failure_handler	= (rclp_callback_t)osl_cmd_pkt_success_handler;
	rclp->status = STS_SUCCESS;

	set_dma_direction( scp, rclp, &dma_dir );

	sp->cdb_len	= scp->cmd_len;
	memcpy(sp->u.lsi_cdb, scp->cmnd, scp->cmd_len);
	set_start_blk_and_blk_count( scp, rclp);
	
	lin_set_rclp_ld_ptr(linux_adp->adp, rclp, &rclp->device_id);
	is_nonld_or_iocmd = rcl_is_nonld_or_iocmd(linux_adp->adp, rclp);  

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)
	rclp->buffer_len		= scsi_bufflen(scp);
	if ( (rc = setup_sglist_each_sg( scp, rclp, is_nonld_or_iocmd , linux_adp)))
	  return rc;

#else
	rclp->buffer_len		= scp->request_bufflen;
	if( LSI_TRUE ==  (rc = setup_zero_length_sglist( scp, rclp, dev, dma_dir, is_nonld_or_iocmd )))
	  return rc;

	if( LSI_FALSE == (rc = setup_multi_length_sglist( scp, rclp, dev, dma_dir, is_nonld_or_iocmd, linux_adp )))
	  return rc; 
#endif

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
	// If this is a RAID 5 IO, or a non-IO command, we would require virtual address
	// of the data transfer buffer. Return LSI_FALSE to indicate this.
	if ( rcl_is_r5rw_or_noniocmd(linux_adp->adp, rclp)){
		megasr_clist_append(&rclp->list, &linux_adp->build_io_list_in);
		rc = LSI_FALSE;
		schedule_work(&linux_adp->work_build_io_in);
	}
#endif
	return rc;
}

static void_t osl_destroy_all_timers(linux_adp_t *lxadp)
{
	uint8_t		i;

	con_log(CL_INFO, ("megaswr: osl_destroy_all_timers\n"));
	for (i= 0; i < MAX_TIMERS; i++) {
		if(lxadp->timer_objects[i].active_flag) {
			del_timer(&(lxadp->timer_objects[i].timer));
			lxadp->timer_objects[i].active_flag	= 0;
			lxadp->timer_count --;
			con_log(CL_ERR, ("megaswr[osl]: timer[%d] cleared\n", i));
		}
	
		lxadp->timer_objects[i].used_flag	= 0;
	}
}

/**
 *@brief
 * Timer timeout function call to terminate the timer scheduled by the other
 * layers
 * 
 *@param	data	: Pointer to timer object structure. 
 *@return	None 
 */
void osl_timeout(unsigned long data)
{
	timer_object_t*	to = (timer_object_t*) data;
	to->active_flag = 0;
	to->timer_fn(to->context1, to->context2); 
}

void  osl_scu_timeout(unsigned long data)
{
    timer_object_t* to= (timer_object_t*) data;
	con_log(CL_INFO, (" [SCU]:Function Name: osl_scu_timeout %p\n", to));
	to->active_flag = 0; //TODO
    to->scu_timer_fn(to->context1);
}


/**
 *@brief
 * Function used to allocate pools of memory to satisfy RAID5 memory requirements when
 * a RAID5 LD is detected or created. The caller should not expect this memory to be
 * available immediately, hence the reason the callback exists. The caller's callback
 * will be invoked once the memory has been allocated, virtual addresses have been mapped,
 * and the SGL has been added to the memory manager, ready for use. Please note that the
 * caller is not guaranteed to get the total amount of memory requested; when this function returns
 * to the caller, the size allocated will be passed back, informing that subsystem
 * about how much memory the OS had available at that time. The caller then can retry for any
 * remaining amount at a later time if they choose to. The callback is invoked after the memory
 * that could be allocated is added to the memory manager and is ready for use.
 * 
 *@param	os_context		: Pointer to os context data.
 *@param	size_in_bytes	: Amount of memory in bytes to allocate and add to memory manager.
 *@param size_allocated	: OS may not give us the requested amount of memory, the amount we are able to get will be returned in this parameter.
 *@param	callback		: Callback routine to notify when memory is available.
 *@param callback_context: Callback routine's context data.
 *
 *@return LSI_TRUE:	A portion or all of the requested memory has been allocated and the callback
 *					will be invoked when memory is ready for use. Caller should not assume that
 *					the memory is available and/or that all of the memory could be allocated
 *					simply because LSI_TRUE was returned.
 *		LSI_FALSE:	No memory could be allocated, not even a portion of the requested size.
 */
boolean_t osl_allocate_pool(pvt_data_t *os_context, uint32_t size_in_bytes, uint32_t *size_allocated, mem_added_fn_t callback, pvt_data_t *callback_context)
{
	linux_adp_t			*lxadp	= (linux_adp_t *)lin_get_linux_adp(os_context);
	linux_dma_mem_t		*dma = &lxadp->dma;
	cmn_sgl_t			dyn_sgl;
	uint32_t			remaining_allocation = size_in_bytes;
	uint32_t			try_size;
	uint8_t				alloc_flag = LINUX_DMA_NOWAIT;

	/* Validate data passed in */
	if (!size_in_bytes) {
		con_log(CL_ERR, ("megaswr[osl]:  err allocate_pool called with zero size to allocate!\n"));
		return LSI_FALSE;
	}
	if (!size_allocated) {
		con_log(CL_ERR, ("megaswr[osl]: err allocate_pool called with null pointer to return size allocated!\n"));
		return LSI_FALSE;
	}
	if (!callback) {
		con_log(CL_ERR, ("megaswr[osl]:  err allocate_pool called without a callback!\n"));
		return LSI_FALSE;
	}

	try_size = LSI_RAID_1_MB_SIZE;
	while (remaining_allocation) {

		if (try_size > remaining_allocation) {
			try_size = remaining_allocation;
		}

		if (dma->list_count >= MAX_MEM_POOLS_PER_POOL_TYPE) {
			con_log(CL_ERR, ("megasr[osl]:  memory allocation failed, overflow\n"));
			return LSI_FALSE;
		}

		if (megasr_dma_alloc(lxadp->pdev, try_size, &dma->list[dma->list_count],  alloc_flag) != LSI_TRUE) {
			try_size /= 2;
			if (try_size < (LSI_RAID_1_MB_SIZE / 8)) {
				/* i cannot try any more and giving up */
				con_log(CL_DEBUG, ("megasr[osl]:  warning, driver allocated %#x bytes less than requested memory for dynamic\n", size_in_bytes));
				break;
			}
			continue;
		}
		
		dma->list[dma->list_count].len = try_size;

		oss_memset((uint8_t *)&dyn_sgl, sizeof(cmn_sgl_t), 0);

		dyn_sgl.sge_count = 1;
		dyn_sgl.sge[0].length = try_size;
		dyn_sgl.sge[0].vaddr = dma->list[dma->list_count].vir;
		dyn_sgl.sge[0].paddr = (uint8_t *)(unsigned long)dma->list[dma->list_count].dma_addr;	// This is broken till we are capable of supported dma >4GB on 32-bit plaforms :-(
		
		if (mem_mgr_add_sgl_mem(lxadp->adp, &dyn_sgl, callback_context) == LSI_FALSE) {

			con_log(CL_ERR, ("megasr[osl]: can't add mem to sgl pool for dynamic buffers [%#x:%#x]\n",
					size_in_bytes, remaining_allocation));

			megasr_dma_free(lxadp->pdev, &lxadp->dma.list[lxadp->dma.list_count]);
			break;
		}

		remaining_allocation	-= try_size; 
		try_size				= LSI_RAID_1_MB_SIZE;
		dma->list_count++;
	}

	*size_allocated = size_in_bytes - remaining_allocation;

	if (*size_allocated) {
		callback(os_context, callback_context);
		return LSI_TRUE;
	}
	else {
		return LSI_FALSE;
	}
}

boolean_t osl_alloc_mem_for_fw_download(pvt_data_t *os_context, uint32_t size_in_bytes, alloc_mem_fn_t callback)
{
	linux_adp_t 		*lxadp	= (linux_adp_t *)lin_get_linux_adp(os_context);
	cmn_sgl_t			dyn_sgl;
	uint32_t			remaining_allocation = size_in_bytes;
	uint32_t			try_size;
	uint16_t			i=0;
	uint32_t			allocatedDmaBufSize=0, tmp_remaining_size=0, pointer_position=0;

	con_log(CL_DEBUG, ("megasr[osl]:%s  \n",__FUNCTION__));

	/* Validate data passed in */
	if (!size_in_bytes) {
		con_log(CL_ERR, ("megaswr[osl]:  err allocate_mem called with zero size to allocate!\n"));
		return LSI_FALSE;
	}
	
	if (!callback) {
		con_log(CL_ERR, ("megaswr[osl]:  err allocate_mem called without a callback!\n"));
		return LSI_FALSE;
	}

	/* Compute the current allocated memory size */
	for(i=0; i<MAX_DMA_BUF; i++)
	{
		if(lxadp->dma_buf.buf_list[i].len) {
			allocatedDmaBufSize += lxadp->dma_buf.buf_list[i].len;
		} else {
			break;
		}
	}

	/* Check if the Required allocation is already met. If required memory
	 * is not allocated try allocation here.
	 */
	if(allocatedDmaBufSize < size_in_bytes) {

		remaining_allocation = try_size = (size_in_bytes - allocatedDmaBufSize);

		while(remaining_allocation && lxadp->dma_buf.list_count < MAX_DMA_BUF) { 
			if (megasr_dma_alloc(lxadp->pdev, try_size, &lxadp->dma_buf.buf_list[lxadp->dma_buf.list_count], LINUX_DMA_NOWAIT) != LSI_TRUE) {

				con_log(CL_INFO, ("megaswr[osl]: err ioctl module memory allocation failed\n"));	

				/* Do not fail the adapter loading. Another memory size would be attempted. So break out of the loop */
				try_size -= LSI_RAID_64_KB_SIZE;

				if(try_size < LSI_RAID_64_KB_SIZE) {
					break;
				} else {
					continue;
				}
			} else {
				lxadp->dma_buf.list_count++;
				remaining_allocation -= try_size;
				try_size = remaining_allocation;
			}
		}

		/* Check if the memory alloaction requirement is met. If not
		 * fail thePD f/w donwload operation.
		 */

		if(remaining_allocation != 0) {
			rcl_fw_dwld_app_abort(lxadp->adp, 0);
			rcl_ioctl_set_adp_cmd_unblock(lxadp->adp, 0);
			return LSI_FALSE;
		}
	}

	/* Now assign the allocated memory in chunks of 64k into the
	 * fw_download structure.
	 */

	try_size = LSI_RAID_64_KB_SIZE;
	remaining_allocation = size_in_bytes;

	for(i=0; i<lxadp->dma_buf.list_count; i++)
	{
		if(remaining_allocation == 0)
			break;

		memset(lxadp->dma_buf.buf_list[i].vir, 0, lxadp->dma_buf.buf_list[i].len);
		tmp_remaining_size = lxadp->dma_buf.buf_list[i].len;

		pointer_position = 0;

		while(tmp_remaining_size >= LSI_RAID_64_KB_SIZE) {

			oss_memset((uint8_t *)&dyn_sgl, sizeof(cmn_sgl_t), 0);

			dyn_sgl.sge_count = 1;
			dyn_sgl.sge[0].length = try_size;
			dyn_sgl.sge[0].vaddr = lxadp->dma_buf.buf_list[i].vir + pointer_position;
			dyn_sgl.sge[0].paddr = (uint8_t *)(unsigned long)lxadp->dma_buf.buf_list[i].dma_addr + pointer_position;  // This is broken till we are capable of supported dma >4GB on 32-bit plaforms :-(

			lin_add_allocated_sgl_mem(lxadp->adp, &dyn_sgl);

			remaining_allocation -= try_size;
			tmp_remaining_size -= try_size;
			pointer_position += LSI_RAID_64_KB_SIZE;

			if(remaining_allocation == 0)
				break;
		}
	}
    
	//*size_allocated = size_in_bytes - remaining_allocation;

	if (remaining_allocation == 0) {
		callback(os_context);
		return LSI_TRUE;
	} else {
		rcl_fw_dwld_app_abort(lxadp->adp, 0);
		rcl_ioctl_set_adp_cmd_unblock(lxadp->adp, 0);
		return LSI_FALSE;
	}
}


/*boolean_t osl_free_mem_from_fw_download(pvt_data_t *os_context,uint32_t size_in_bytes)
{
   linux_adp_t 		*lxadp	= (linux_adp_t *)lin_get_linux_adp(os_context);
   linux_dma_mem_list_t linux_dma;
   cmn_sgl_t		dyn_sgl;
   uint32_t 		remaining_allocation = size_in_bytes;
   uint32_t 		try_size;
   uint8_t          count=0;
	   

   
	if (!size_in_bytes) {
		con_log(CL_ERR, ("megaswr[osl]:  err free_mem called with zero size to free!\n"));
		return LSI_FALSE;
	}

	con_log(CL_DEBUG, ("megasr[osl]:%s  \n",__FUNCTION__));
	try_size = LSI_RAID_64_KB_SIZE;
	while (remaining_allocation) {

		if (try_size > remaining_allocation) {
			try_size = remaining_allocation;
		}

        lin_get_sgl_mem_to_free(lxadp->adp, &dyn_sgl,count);
		linux_dma.dma_addr = (dma_addr_t)(unsigned long)dyn_sgl.sge[0].paddr;

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
		dma_free_coherent(&lxadp->pdev->dev, (size_t)try_size,dyn_sgl.sge[0].vaddr, linux_dma.dma_addr);
#else
		pci_free_consistent(lxadp->pdev, (size_t)try_size,dyn_sgl.sge[0].vaddr, linux_dma.dma_addr);
#endif
				   
		remaining_allocation  -= try_size; 
		try_size 	       = LSI_RAID_64_KB_SIZE;
                count++;
	}
	
    return LSI_TRUE;   
}
*/

/**
 *@brief 
 * Free the memory alloctaed for fw_download.
 *
 *@param	os_cxt	
 *@param	size_in_bytes
 *@return	LSI_TRUE if success 
 */

boolean_t osl_free_mem_from_fw_download(pvt_data_t *os_context,uint32_t size_in_bytes) {

	mem_free_st_t.os_context    =   os_context;
	mem_free_st_t.size_in_bytes =	size_in_bytes;


	if (!size_in_bytes) {
		con_log(CL_ERR, ("megaswr[osl]:  err free_mem called with zero size to free!\n"));
		return LSI_FALSE;
	}

	con_log ( CL_INFO, ( "megasr[osl]: schduling mem_free\n"));

	//Do not invoke Memory free. As the memory allocated would be freed
	//only during driver unload. 
	//tasklet_schedule ( &mf_tasklet );	//run at kernal's convenience, but very soon

	return LSI_TRUE;
}


/** tasklet defined here **/

void mem_fre_tasklet ( unsigned long mfst ){
	
	mem_fr_st_t *var = (mem_fr_st_t * )( mfst );

	pvt_data_t *os_context = var -> os_context;
	uint32_t size_in_bytes = var -> size_in_bytes;


   linux_adp_t 		*lxadp	= (linux_adp_t *)lin_get_linux_adp(os_context);
   linux_dma_mem_list_t linux_dma;
   cmn_sgl_t		dyn_sgl;
   uint32_t 		remaining_allocation = size_in_bytes;
   uint32_t 		try_size;
   uint8_t          count=0;
	   


	con_log(CL_DEBUG, ("megasr[osl]:%s  \n",__FUNCTION__));
	try_size = LSI_RAID_64_KB_SIZE;
	while (remaining_allocation) {

		if (try_size > remaining_allocation) {
			try_size = remaining_allocation;
		}

        lin_get_sgl_mem_to_free(lxadp->adp, &dyn_sgl,count);
		linux_dma.dma_addr = (dma_addr_t)(unsigned long)dyn_sgl.sge[0].paddr;

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
		dma_free_coherent(&lxadp->pdev->dev, (size_t)try_size,dyn_sgl.sge[0].vaddr, linux_dma.dma_addr);
#else
		pci_free_consistent(lxadp->pdev, (size_t)try_size,dyn_sgl.sge[0].vaddr, linux_dma.dma_addr);
#endif
				   
		remaining_allocation  -= try_size; 
		try_size 	       = LSI_RAID_64_KB_SIZE;
        count++;
	}

}

/**
 *@brief 
 * Complete the given rcl packet to the OS With SUCCESS state.
 *
 *@param	os_cxt	Complete adapter soft-state
 *@param	rclp	RCL packet pointer
 *@return	LSI_TRUE always	
 */
void_t osl_cmd_pkt_success_handler(pvt_data_t* os_cxt, pvt_data_t* rclp_ptr)
{
	rcl_packet_public_t		*rclp = (rcl_packet_public_t *)rclp_ptr;
	struct scsi_cmnd	*scp = (struct scsi_cmnd *)rclp->os_packet;
	linux_adp_t 		*lxadp ;
	uint8_t				complete_in_dpc;
	uint32_t		is_nonld_or_iocmd;
	uint8_t			sg_cnt;

	lxadp = (linux_adp_t*)lin_get_linux_adp(os_cxt); // at this point Lin OSL do not have Linux adapter and so we need to use this call
	is_nonld_or_iocmd	= rcl_is_nonld_or_iocmd(lxadp->adp, rclp);


	if (rclp->status == STS_SUCCESS) {
		scp->result	= 0;
	}
	else {
		switch(rclp->status) {
		case STS_UNSUPPORTED:	// FALL THROUGH
		case STS_NO_DEVICE:
			scp->result = (DID_BAD_TARGET << 16); 
			break;

		/* handle media error properly */
		case STS_MEDERR:
			osl_cmn_fill_sense_info_from_rclp(rclp_ptr, (void_t*)scp->sense_buffer, SCSI_SENSE_BUFFERSIZE);
			scp->result = (DRIVER_SENSE << 24) | (DID_ERROR << 16) | (CHECK_CONDITION << 1);
			break;

		case STS_CD_DVD_ERROR:
			scp->result = (DRIVER_SENSE << 24) | (DID_OK << 16) | (CHECK_CONDITION << 1);
			break;

		case STS_CLEAN_ABORT:	// FALL THROUGH
		case STS_DIRTY_ABORT:	// FALL THROUGH
		case STS_TIME_OUT:		// FALL THROUGH
		case STS_ERROR:			// FALL THROUGH
		case STS_RESET:			// FALL THROUGH
		default:
			if(rclp->scsi_status == SCSISTAT_CHECK_CONDITION)
				scp->result = (DRIVER_SENSE << 24) | (DID_OK << 16) | (CHECK_CONDITION << 1);
			else
				scp->result = (DID_ERROR << 16);
			break;
		}

		scp->result |= rclp->scsi_status;

        if (rclp->device_id.bus <= LD_BUS && rclp->sense_len)
            oss_memcpy((uint8_t*)&rclp->sense_info, scp->sense_buffer, SCSI_SENSE_BUFFERSIZE);
	}

	complete_in_dpc = 0;

/* SG related changes are consistent from (2,6,27) */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)
	sg_cnt = rclp->bkup_sge_count;
#else
	sg_cnt = scp->use_sg;
#endif
	// If this packet requires kunmap, schedule DPC for it
	if (sg_cnt && (rcl_is_r5rw_or_noniocmd(lxadp->adp, rclp)) ) {
		megasr_clist_append(&rclp->list, &lxadp->build_io_list_out);
		complete_in_dpc = 1;

		/* Don't reschedule the dpc if it is already done; but if this is the last command we are ever going
		 * to get, then the DPC may not process this. To avoid this, we will check if this build_io_list_out
		 * once more before exiting DPC
		 */
		if (!lxadp->dpc_scheduled) {
			lxadp->dpc_scheduled = 1;
			schedule_work(&lxadp->work_build_io_out);
		}
	}

	if (!complete_in_dpc) {
	  linux_unmap_sg( lxadp, rclp );		
		scp->host_scribble = NULL;
		scp->scsi_done(scp);

		rcl_free_rcl_packet(os_cxt, rclp);
	}

	/* flush commands in schedule queue */
	osl_cmn_flush_schedule_queue(os_cxt);
}
uint32_t lin_get_host_no(linux_adp_t * lxadp)
{
	return lxadp->host->host_no;
}


pvt_data_t *osl_get_adapter(uint32_t index)
{
    return (adps_g[index]);
}

addr_t *osl_get_os_ext(pvt_data_t *os_context)
{
	/* No Need to implement this function for Linux */
	return 0;
}

void osl_set_busy(pvt_data_t *os_context)
{
	lin_osl_set_busy(os_context);
}

void osl_unset_busy(pvt_data_t *os_context)
{
	lin_osl_unset_busy(os_context);
}

megasr_intr_type_e osl_get_interrupt_mode(pvt_data_t *os_context)
{
	linux_adp_t	*lxadp      = (linux_adp_t *)lin_get_linux_adp(os_context);
	// 1 represents MSIX mode and 0 means Legacy IRQ
	if (megasr_lib_get_msix_mode(lxadp->adp)) return INTR_TYPE_MSIX;
	else return INTR_TYPE_LEGACY;
}

int megasr_printk(const char *fmt, ...)
{
	va_list		ap;
	char		buf[768];
        char *fmt1 = (char *)fmt;
	va_start(ap, fmt);
	//vsprintf(buf, fmt, ap);
        vsnprintf(buf, sizeof(buf), fmt1, ap);
	va_end(ap);
	//return printk(buf);
         //return printk(KERN_ERR":%s\n",buf);
         return printk(KERN_ERR":%s",buf);
}

module_init(lsraid_init);
module_exit(lsraid_exit);

/* vim: set ts=4 sw=4 tw=78 ai si: */
